文章目录
- 🛫 导读
- 需求
- 开发环境
- 升级优化(vs2019)
- 相关地址
- Blackbone工程中的lib库添加
- Blackbone工程修改
- tools工程修改
- 旧文章整理(vs2017)
- 功能描述
- 内存读取-BlackBone库的集成
- 内存读取-检测参数
- 内存读取-ReadProcessMemory
- 内存读取-BlackBone_R3
- 内存读取-BlackBone_R0
- 内存读取-BlackBone_R0-测试
- 内存读取-三种方式比较
- 📖 参考资料
🛫 导读
需求
驱动一直没有系统的学习过,每次用到的时候总得折腾下安装环境,现在整理下,避免以后再各种查资料。
经常遇到一些软件被保护起来,无法读取其内存数据,今天将基于开源库BlackBone,编写一个简单的内存读写工具。
课程目标
- 编写工具,实现内存读写
- BlackBone库的集成
- 比较常见内存读取的方式
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2019-05-21 | 最早发布在看雪上,现在整理一下 |
操作系统 | win7-x64虚拟机 | |
VS2017 community | ||
无签名驱动加载工具 | win64udl_exe.exe | |
WDK | 10.0.17763.1 | |
32位应用程序 | (开发的目标程序是32位的,所以暂时只支持32位) | |
64位的驱动程序 | ||
升级优化(vs2019)
相关地址
之前代码放在github上,每次下载都不方便,所以将代码移到了
gitcode
上了。
- 本工具工程gitcode代码地址:https://gitcode.net/yemao/kernel-win/MyOpen
- 本工具工程代码上传了BlackBone的VS2017版本的代码,地址为https://gitcode.net/yemao/kernel-win/MyOpen/third/Blackbone/Blackbone.sln。
- 本工具工程代码地址为https://gitcode.net/yemao/kernel-win/MyOpen/course/WinDriver/tools/tools.sln。
Blackbone工程中的lib库添加
之前的项目,为了节省项目空间,lib和dll等库都未提交到git上,这次补上了,包含
BeaEngine
和DIA
相关的资源。
Blackbone工程修改
- 升级工具集到2019、C++语言标准为C++17(其他版本会报错)
- 警告等级从4降到
3
。
- 生成后事件中的路径有问题,需要调整下,如下图:
- 修复
error C2039: “inserter”: 不是“std”的成员
报错。
third/Blackbone/src/BlackBone/Process/ProcessModules.cpp
中需要增加头文件#include <iterator>
。
注意事项: 所有修改只更改了Debug-Win32的选项,所以编译的时候,需要选择该项目。
而且vs2019不再支持XP,所以Debug(XP)
选项已经失效了。
tools工程修改
- 升级工具集到2019、C++语言标准为C++17(其他版本会报错)
2.引用的lib库中,
(XP)
已经没有了,去掉。
旧文章整理(vs2017)
功能描述
这里写了个工具,界面如下图所示:
主要功能:
- 工具启动的时候会填充
进程列表Combo控件
- 点击
进程列表Combo控件
,会更新进程列表 - 进程列表中的项格式为:
[pid][进程类型:x86/x64] 进程全路径
- 选中进程,更新控件
地址(0x)
对应的地址内容 - 设置读取方式:ReadProcessMemory、BlackBone_R3、BlackBone_R0
- 读取并以16进制显示内存内容
工具会根据“读取方式”,分别执行Windows API、BlackBone API、BlackBone驱动三种方式的内存读取,满足不同的使用场景。
内存读取-BlackBone库的集成
本工具工程github代码地址:https://github.com/ninecents/MyOpen
本工具工程代码上传了BlackBone的VS2017版本的代码,地址为https://github.com/ninecents/MyOpen/third/Blackbone。
本工具工程代码地址为https://github.com/ninecents/MyOpen/course/WinDriver/tools/tools.sln。
不了解BlackBone的,可以参考BlackBone介绍。
驱动环境搭建可参考 [Windows驱动开发] 00_环境搭建 。
集成步骤:
-
打开工程BlackBone.sln和BlackBoneDrv.sln并编译,可以获得相应的库文件和驱动文件。这里编译选项选择Debug(XP)的,用来支持XP系统。
-
tools-MFC工程中“C/C++ -> 命令行”,添加Blackbone路径
/I"../../../../third/Blackbone/src"
-
代码中引用相关头文件,如下所示:
#include <BlackBone/Config.h>
#include <BlackBone/Process/Process.h>
#include <BlackBone/Process/MultPtr.hpp>
#include <BlackBone/Process/RPC/RemoteFunction.hpp>
#include <BlackBone/PE/PEImage.h>
#include <BlackBone/Misc/Utils.h>
#include <BlackBone/Misc/DynImport.h>
#include <BlackBone/Syscalls/Syscall.h>
#include <BlackBone/Patterns/PatternSearch.h>
#include <BlackBone/Asm/LDasm.h>
#include <BlackBone/localHook/VTableHook.hpp>
#include <BlackBone/DriverControl/DriverControl.h>
#ifdef _DEBUG
#pragma comment(lib, "../../../../third/Blackbone/build/Win32/Debug(XP)/BlackBone.lib")
#else
#pragma comment(lib, "../../../../third/Blackbone/build/Win32/Release(XP)/BlackBone.lib")
#endif
- 调用BlackBone库函数,下面为枚举进程列表的代码
void CtoolsMFCDlg::OnCbnDropdownComboProcess()
{
m_combo_process.ResetContent();
blackbone::Process process;
std::vector<DWORD> vct_pids = blackbone::Process::EnumByName(L"");
for (auto pid : vct_pids)
{
NTSTATUS status = process.Attach(pid);
CString msg;
if (NT_SUCCESS(status))
{
if (process.modules().GetMainModule())
{
CString str_64 = process.core().isWow64() ? _T("x86") : _T("x64");
msg.Format(_T("[%05d][%s] %s"), process.pid(), str_64.GetBuffer(), process.modules().GetMainModule()->fullPath.data());
}
else
{
msg.Format(_T("[%d] %s"), process.pid(), _T("failed_get_path"));
}
}
else
{
msg.Format(_T("[%d] %s"), pid, _T("failed_Attach"));
}
m_combo_process.AddString(msg);
int nIndex = m_combo_process.GetCount() - 1;
m_combo_process.SetItemData(nIndex, pid);
}
}
内存读取-检测参数
点击读取按钮,先检测参数,具体代码如下所示:
void CtoolsMFCDlg::OnBnClickedButtonRead()
{
// 更新控件数据,清空16进制显示控件内容
UpdateData();
m_mem_data.SetString(_T(""));
// 获取目标进程内存地址:ll_address
CString str_address;
m_mem_address.GetWindowText(str_address);
str_address = _T("0x") + str_address;
LONGLONG ll_address = _tcstoull_l(str_address.GetBuffer(), NULL, 16, 0);
// 获取目标进程ID:pid
int nIndex = m_combo_process.GetCurSel();
DWORD pid = m_combo_process.GetItemData(nIndex);
// 获取读取方式:str_read_type
CString str_read_type;
m_combo_read_type.GetWindowText(str_read_type);
// 打开目标进程
BlackBone::Process process;
process.Attach(pid);
if (!process.valid())
{
AfxMessageBox(_T("打开进程失败。"));
return;
}
......
}
内存读取-ReadProcessMemory
ReadProcessMemory实现很简单,直接调用该函数即可:
SIZE_T byte_read;
BOOL result = ReadProcessMemory(process.core().handle(), (LPCVOID)ll_address, (LPVOID)bytes, (SIZE_T)m_mem_length, &byte_read);
查看ReadProcessMemory函数原型:
WINBASEAPI
BOOL
WINAPI
WriteProcessMemory(
__in HANDLE hProcess,
__in LPVOID lpBaseAddress,
__in_bcount(nSize) LPCVOID lpBuffer,
__in SIZE_T nSize,
__out_opt SIZE_T * lpNumberOfBytesWritten
);
参数 lpBaseAddress 的类型是LPVOID,对于32位程序,该值只有4字节,读取64位进程就只能看运气了,如果地址大于4字节,读取的结果是错的(也可能直接失败)。比如,我们读取64位的chrome进程,由于chrome.exe模块地址0x13F630000大于4字节,ReadProcessMemory显示错误的内容。
内存读取-BlackBone_R3
BlackBone_R3方式实现也很简单,直接调用BlackBone的api函数即可:
NTSTATUS status = process.memory().Read(ll_address, m_mem_length, (PVOID)bytes);
BlackBone_R3方式最终调用了Nt函数NtWow64ReadVirtualMemory64(32位的Windows操作系统不知道是不是下面的调用关系),调用关系如下:
内存读取-BlackBone_R0
BlackBone_R0方式实现如下:
// 加载驱动
NTSTATUS status = blackbone::Driver().EnsureLoaded();
// 驱动内存读取
status = blackbone::Driver().ReadMem(pid, ll_address, m_mem_length, (PVOID)bytes);
最终R3层调用了DriverControl::ReadMem函数,对应控制码是IOCTL_BLACKBONE_COPY_MEMORY。
而R0层调用了BBCopyMemory函数,而该函数调用了内核API函数MmCopyVirtualMemory实现内存读写。
内存读取-BlackBone_R0-测试
打开虚拟机,将win64udl_exe.exe、tools-MFC.exe、BlackBoneDrv7.sys拷贝到虚拟机中,运行win64udl_exe.exe程序(不要关闭,保证未签名的BlackBoneDrv7.sys可以正常加载):
打开tools-MFC.exe,选择任意进程,选择读取方式为“blackbone_R0”,点击读取按钮:运行结果如下图所示:
内存读取-三种方式比较
读取方式 | 缺陷 | 备注 |
---|---|---|
ReadProcessMemory | 32位进程调用该api不能读取64位进程内存 | |
BlackBone_R3 | 权限问题 | |
BlackBone_R0 | 完美(稳定性、防检测) | |
📖 参考资料
- 本工具工程gitcode代码地址:https://gitcode.net/yemao/kernel-win/MyOpen
- Tesla.Angela大神的WIN64驱动编程基础教程
- 纯C++编写Win32/X64通用Shellcode注入csrss进程
ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。