当前位置: 首页 > news >正文

MemoryModule - 应用编程细节

文章目录

    • 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 _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&#xff0c;在上下文中加入DLL在内存载入前的信息MemoryModule.hMemoryModule.cpp实现接口MemoryGetPayload() 整理 - 在内存载入的DLL中&#xff0c;取得资源表中的信息&#xff0c;取得载入前的DLL内容…...

Java程序CPU持续高,如何排查?

首先找到进程ID jps然后找到该进程用占用cpu高的线程 top -Hp 进程ID将线程ID转化为十六进制 printf “0x%x” 线程ID使用jstack 工具跟踪堆栈定位问题 jstack 进程ID | grep 十六进制线程ID -A 5说明&#xff0c;最后-A 5是打印出来后5行。...

(Java)心得:LeetCode——15.三数之和

一、原题 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。…...

Rust中忽略JSON反序列化时的不必要字段

在Rust中处理JSON数据时&#xff0c;经常会遇到JSON数据中包含一些在目标数据结构中不存在的字段的情况。如果你使用的是serde和serde_json这两个流行的库来处理JSON&#xff0c;那么有一些简单的方法可以忽略这些不必要的字段。 默认行为&#xff1a;忽略未知字段 在Rust中&…...

UDP多对多组播通信

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

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 调用堆栈可以很好的帮助我们定位问题&#xff0c;特别是报错的Error日志 Debug.Log("Hello World");Debug.Log("This is a log m…...

二叉树的常见操作

建立树 复制二叉树 计算深度 计算总结点数 计算叶子结点数...

CSS 根据子元素选择父元素,并设置父元素的样式

场景举例&#xff1a;当子元素有增加了一个class时&#xff0c;需要影响其父元素的样式 可以使用":has"伪类来实现选择父元素的效果 <style>.parent:has(.child){background-color: #eee;}p{width:100px;border:1px solid #000;} </style> <body>…...

onnx转trt时,关于动态shape自动配置默认值的脚本

onnx转trt时&#xff0c;关于动态shape自动配置默认值&#xff0c;一般需要指定3个shape&#xff0c;分别是最小最优与最大。但是我们在测试时不想写那么多的代码&#xff0c;能否自动实现3个shape的配置&#xff0c;这里实现了一版。 import osimport tensorrt as trt import…...

实验室无法培养的菌,原来可以这么研究!

厌氧氨氧化&#xff08;anammox&#xff09;细菌在全球氮循环和废水氮去除中发挥着至关重要的作用&#xff0c;由于anammox细菌生长缓慢、难以培养等特点&#xff0c;对其生态学和生物学特性知之甚少。近日&#xff0c;凌恩生物合作客户重庆大学陈猷鹏教授团队在《Science of t…...

Xed编辑器开发第一期:使用Rust从0到1写一个文本编辑器

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

农业自动气象监测站:赋能智慧农业的新动力

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

2-6 任务 猜数小游戏(单次版)

本任务要求编写一个猜数小游戏&#xff08;单次版&#xff09;&#xff0c;游戏规则是计算机产生一个0到100之间的随机整数&#xff0c;用户通过输入猜测的数字进行猜测&#xff0c;根据猜测情况给出提示&#xff0c;直到猜对为止。编程思路是利用while循环和多分支结构实现永真…...

springboot 定时任务解决方案

Scheduled (springboot 自带的 注解) 基于注解Scheduled默认为单线程&#xff0c;开启多个任务时&#xff0c;任务的执行时机会受上一个任务执行时间的影响。 EnableScheduling注解&#xff1a; 在配置类上使用&#xff0c;开启计划任务的支持&#xff08;类上&#xff09;。…...

谷粒商城实战(024 业务-订单模块-分布式事务1)

Java项目《谷粒商城》架构师级Java项目实战&#xff0c;对标阿里P6-P7&#xff0c;全网最强 总时长 104:45:00 共408P 此文章包含第284p-第p290的内容 简介 模拟积分服务出异常&#xff0c;前方的锁库存事务未回滚&#xff0c;这时候就需要分布式事务 本地事务 事务的隔离…...

.NET使用Microsoft.IdentityModel.Tokens对SAML2.0登录断言校验

如题。使用SAML单点登录对IDP返回的Response断言使用微软提供的Microsoft.IdentityModel.Tokens对断言&#xff08;Assertion&#xff09;进行校验。 首先需要安装Muget包&#xff0c;Microsoft.IdentityModel.Tokens和Microsoft.IdentityModel.Tokens.Saml。 简易示例代码如…...

性能测试学习二

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

小丑的身份证和复印件 (BFS + Floyd)

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

C++类与对象(上)

C类与对象 面向过程和面向对象初步认识类的引入类的定义类的两种定义方式&#xff1a; 类的访问限定符及封装访问限定符 封装类的作用域类的实例化类对象模型如何计算类对象的大小结构体内存对齐规则&#xff1a; this指针 面向过程和面向对象初步认识 C语言是面向过程的&…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

加密通信 + 行为分析:运营商行业安全防御体系重构

在数字经济蓬勃发展的时代&#xff0c;运营商作为信息通信网络的核心枢纽&#xff0c;承载着海量用户数据与关键业务传输&#xff0c;其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级&#xff0c;传统安全防护体系逐渐暴露出局限性&a…...