10.3 调试事件转存进程内存

这篇具有很好参考价值的文章主要介绍了10.3 调试事件转存进程内存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我们继续延申调试事件的话题,实现进程转存功能,进程转储功能是指通过调试API使获得了目标进程控制权的进程,将目标进程的内存中的数据完整地转存到本地磁盘上,对于加壳软件,通常会通过加密、压缩等手段来保护其代码和数据,使其不易被分析。在这种情况下,通过进程转储功能,可以将加壳程序的内存镜像完整地保存到本地,以便进行后续的分析。

在实现进程转储功能时,主要使用调试API和内存读写函数。具体实现方法包括:以调试方式启动目标进程,将其暂停在运行前的位置;让目标进程进入运行状态;使用ReadProcessMemory函数读取目标进程内存,并将结果保存到缓冲区;将缓冲区中的数据写入文件;关闭目标进程的调试状态。

首先老样子先来看OnException回调事件,当进程被断下时首先通过线程函数恢复该线程的状态,在进程被正确解码并运行起来时直接将该进程的EIP入口地址传递给MemDump();内存转存函数,实现转存功能;

void OnException(DEBUG_EVENT *pDebug, BYTE *bCode)
{
    CONTEXT context;
    DWORD dwNum;
    BYTE bTmp;

    // 打开当前进程与线程
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pDebug->dwProcessId);
    printf("[+] 当前打开进程句柄: %d 进程PID: %d \n", hProcess, pDebug->dwProcessId);
    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pDebug->dwThreadId);
    printf("[+] 当前打开线程句柄: %d 线程PPID: %d \n", hThread, pDebug->dwThreadId);
    // 暂停当前线程
    SuspendThread(hThread);

    // 读取出异常产生的首地址
    ReadProcessMemory(hProcess, pDebug->u.Exception.ExceptionRecord.ExceptionAddress, &bTmp, sizeof(BYTE), &dwNum);
    printf("[+] 当前异常产生地址为: 0x%08X \n", pDebug->u.Exception.ExceptionRecord.ExceptionAddress);

    // 设置当前线程上下文,获取线程上下文
    context.ContextFlags = CONTEXT_FULL;
    GetThreadContext(hThread, &context);

    printf("[-] 恢复断点前: EAX = 0x%08X  EIP = 0x%08X \n", context.Eax, context.Eip);
    // 将刚才的CC断点取消,也就是回写原始的指令集
    WriteProcessMemory(hProcess, pDebug->u.Exception.ExceptionRecord.ExceptionAddress, bCode, sizeof(BYTE), &dwNum);

    // 当前EIP减一并设置线程上下文
    context.Eip--;
    SetThreadContext(hThread, &context);
    printf("[+] 恢复断点后: EAX = 0x%08X  EIP = 0x%08X \n", context.Eax, context.Eip);
    printf("[+] 获取到动态入口点: 0x%08x \n", pDebug->u.CreateProcessInfo.lpBaseOfImage);
    // 转储内存镜像
    MemDump(pDebug, context.Eip, (char *)"dump.exe");
    // 恢复线程
    ResumeThread(hThread);
    CloseHandle(hThread);
    CloseHandle(hProcess);
}

MemDump函数中,首先通过调用CreateFile函数打开me32.szExePath路径也就是转存之前的文件,通过使用VirtualAlloc分配内存空间,分配大小是PE头中文件实际大小,接着OpenProcess打开正在运行的进程,并使用ReadProcessMemory读取文件的数据,此处读取的实在内存中的镜像数据,当读取后手动修正,文件的入口地址,及文件的对齐方式,接着定位PE节区数据,找到节区首地址,并循环将当前节区数据赋值到新文件缓存中,最后当一切准备就绪,通过使用WriteFile函数将转存后的文件写出到磁盘中;

void MemDump(DEBUG_EVENT *pDe, DWORD dwEntryPoint, char *DumpFileName)
{
    // 得到当前需要操作的进程PID
    DWORD dwPid = pDe->dwProcessId;
    MODULEENTRY32 me32;

    // 对系统进程拍摄快照
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);

    me32.dwSize = sizeof(MODULEENTRY32);
    // 得到第一个模块句柄,第一个模块句柄也就是程序的本体
    BOOL bRet = Module32First(hSnap, &me32);
    printf("[+] 当前转储原程序路径: %s \n", me32.szExePath);

    // 打开源文件,也就是dump之前的文件
    HANDLE hFile = CreateFile(me32.szExePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (hFile == INVALID_HANDLE_VALUE)
        exit(0);

    // 判断PE文件的有效性
    IMAGE_DOS_HEADER imgDos = { 0 };
    IMAGE_NT_HEADERS imgNt = { 0 };

    DWORD dwReadNum = 0;

    // 读入当前内存程序的DOS头结构
    ReadFile(hFile, &imgDos, sizeof(IMAGE_DOS_HEADER), &dwReadNum, NULL);
    // 判断是否是一个合格的DOS头
    if (imgDos.e_magic != IMAGE_DOS_SIGNATURE)
        return;
    // 设置文件指针到NT头上
    SetFilePointer(hFile, imgDos.e_lfanew, 0, FILE_BEGIN);
    ReadFile(hFile, &imgNt, sizeof(IMAGE_NT_HEADERS), &dwReadNum, NULL);
    // 判断是否是合格的NT头
    if (imgNt.Signature != IMAGE_NT_SIGNATURE)
        return;

    // 得到EXE文件的大小
    DWORD BaseSize = me32.modBaseSize;
    printf("[+] 当前内存文件大小: %d --> NT结构原始大小: %d 一致性检测: True \n", BaseSize, imgNt.OptionalHeader.SizeOfImage);

    // 如果PE头中的大小大于实际内存大小,则以PE头中大小为模板
    if (imgNt.OptionalHeader.SizeOfImage > BaseSize)
    {
        BaseSize = imgNt.OptionalHeader.SizeOfImage;
    }

    // 分配内存空间,分配大小是PE头中文件实际大小,并打开进程
    LPVOID pBase = VirtualAlloc(NULL, BaseSize, MEM_COMMIT, PAGE_READWRITE);
    printf("[+] 正在分配转储空间 句柄: %d \n", pBase);

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);

    // 读取文件的数据,此处读取的实在内存中的镜像数据
    bRet = ReadProcessMemory(hProcess, me32.modBaseAddr, pBase, me32.modBaseSize, NULL);

    // 判断PDOS头的有效性
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBase;
    if (pDos->e_magic != IMAGE_DOS_SIGNATURE)
        return;

    // 计算出NT头数据
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (PBYTE)pBase);
    if (pNt->Signature != IMAGE_NT_SIGNATURE)
        return;

    // 设置文件的入口地址
    pNt->OptionalHeader.AddressOfEntryPoint = dwEntryPoint - pNt->OptionalHeader.ImageBase;
    printf("[*] 正在设置Dump文件相对RVA入口地址: 0x%08X \n", pNt->OptionalHeader.AddressOfEntryPoint);

    // 设置文件的对齐方式
    pNt->OptionalHeader.FileAlignment = 0x1000;
    printf("[*] 正在设置Dump文件的对齐值: %d \n", pNt->OptionalHeader.FileAlignment);

    // 找到节区首地址,并循环将当前节区数据赋值到新文件缓存中
    PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PBYTE)&pNt->OptionalHeader + pNt->FileHeader.SizeOfOptionalHeader);
    for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
        pSec->PointerToRawData = pSec->VirtualAddress;
        printf("[+] 正在将虚拟地址: 0x%08X --> 设置到文件地址: 0x%08X \n", pSec->VirtualAddress, pSec->PointerToRawData);
        pSec->SizeOfRawData = pSec->Misc.VirtualSize;
        printf("[+] 正在将虚拟大小: %d --> 设置到文件大小: %d \n", pSec->Misc.VirtualSize, pSec->SizeOfRawData);
        pSec++;
    }
    CloseHandle(hFile);

    // 打开转储后的文件.
    hFile = CreateFile(DumpFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if (hFile == INVALID_HANDLE_VALUE)
        exit(0);
    printf("[*] 转储 %s 文件到本地 \n", DumpFileName);

    DWORD dwWriteNum = 0;

    // 将读取的数据写入到文件
    bRet = WriteFile(hFile, pBase, me32.modBaseSize, &dwWriteNum, NULL);
    if (dwWriteNum != me32.modBaseSize || FALSE == bRet)
        printf("写入错误 !");
    // 关闭于释放资源
    CloseHandle(hFile);
    VirtualFree(pBase, me32.modBaseSize, MEM_RELEASE);
    CloseHandle(hProcess);
    CloseHandle(hSnap);
}

读者可自行运行这段程序,当程序运行后即可将指定的一个文件内存数据完整的转存到磁盘中,输出效果如下图所示;

10.3 调试事件转存进程内存文章来源地址https://www.toymoban.com/news/detail-710403.html

到了这里,关于10.3 调试事件转存进程内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

领支付宝红包 赞助服务器费用

相关文章

  • 【ARM 嵌入式 编译系列 10.3 -- GNU elfutils 工具小结】

    请阅读 【ARM GCC 编译专栏导读】 上篇文章:【ARM 嵌入式 编译系列 10.2 – 符号表与可执行程序分离详细讲解】 下篇文章:【ARM 嵌入式 编译系列 11 – GCC attribute ((packed))详细介绍】 GNU elfutils是一个开源的工具集,用于处理 ELF (Executable and Linkable Format)格式的可执行文件、

    2024年02月13日
    浏览(52)
  • 最新 HomeAssistant OS 10.3 智能家居搭建(树莓派P400)

    首先,我在这儿给大家做个个人总结,我之前在树莓派P400 4G版本上安装过Debian + Home Assistant Core版本,本以为可以继续操作树莓派的其他功能,当电脑用的,但事实上,我高估了树莓派的性能,很卡,所以我最终选择了把整个树莓派直接安装OS版本。虽然可能不太划算,预算是

    2024年02月03日
    浏览(34)
  • gcc-buildroot-9.3.0 和 gcc-arm-10.3 的区别

    gcc-buildroot-9.3.0 和 gcc-arm-10.3 是两个不同的 GCC (GNU Compiler Collection) 版本,主要用于编译 C、C++ 和其他语言的程序。它们之间的区别主要体现在以下几个方面: 版本号:gcc-buildroot-9.3.0 对应的是 GCC 9.3.0 版本,而 gcc-arm-10.3 对应的是 GCC 10.3 版本。版本号的增加通常意味着修复了一

    2024年02月13日
    浏览(38)
  • 银河麒麟V10SP1-20200711的mate-indicators进程占用内存过高的解决办法

    目录 一、监控异常 二、进程异常 三、解决方法 (一)第一步:先查看操作系统版本 (二)第二步:下载相应版本的补丁包 (三)第三步:升级补丁、重启系统   1. 升级步骤   2. 生效方式   3. 回退操作         有一台服务器在运维监控系统出发告警:内存使用率达到

    2024年02月07日
    浏览(218)
  • 【ARM Coresight 系列文章 10.3 - ARM Coresight STM 寄存器介绍 及STM DMA 传输介绍】

    上篇文章:ARM Coresight 系列文章 10.2 - ARM Coresight STM Trace packets STM 的寄存器主要可以分为以下几类: STM DMA 相关的; STM HW Trigger 相关的; 系统控制及状态寄存器; 只读寄存器。 STM DMA 相关的寄存器

    2024年02月15日
    浏览(42)
  • 分享金媒v10.3开源系统中CRM线下客户管理系统使用指南和小程序上架细分流程

    本系统金媒婚恋开源系统v10.3也叫择爱系统目前最新版全开源包括OElove最新版10.0都是最新但是很多客户不动CRM使用流程我就按照流程给大家介绍下如果大家对程序感兴趣也可以分享给你看我昵称里可以Q我,请注明:CSDN网友 ●本系统红娘分两种,一种是全民红娘也就是推广员

    2024年02月22日
    浏览(49)
  • uniapp计算视频学习进程,并且下次回来继续播放(不能快进)

    该功能分别有三个难点: 比如该视频的总时长为120秒,然后现在播放的时长为12秒,计算当前视频学习时长的百分比 比如该视频的总时长为120秒,当前视频学习时长为10%,计算上次播放视频的秒数 到这里第一个难题已经解决 然后想要获取上次播放视频的秒数最佳方法就是请

    2024年02月02日
    浏览(46)
  • ArcGIS安装时报错:提示“arcgis10.3 for Desktop require Microsoft .NET Framework 3.5 sp1或等效环境”的解决方法

    ArcGIS版本在Windows 10系统安装,会出现下图所示情况,究其原因是Windows 10系统安装的Mircrosoft .NET Framework版本为4.0以上高级服务版本。 解决方法: 1.右键电脑,选择属性;控制面板,选择程序 2.启用或关闭Windows功能;在.NET Framework 3.5前面打勾 这样问题就解决了,就可以继续安

    2024年02月11日
    浏览(57)
  • 《逆商》我们该如何应对坏事件

     关于作者 作者保罗·史托兹博士是逆商理论的提出者和奠基人,他曾被《人力资源》杂志评为 “ 全球十大有影响力的思想家 ” 。在二十多年前提出逆商理论之后,他一直在致力于帮助各行各业的人士提高逆商,在实践中积累了该领域大量的数据和经验。 关于本书 本书是

    2024年02月06日
    浏览(37)
  • 从 Uber 数据泄露事件我们可以学到什么?

    Uber 数据泄露始于一名黑客从暗网市场购买属于一名 Uber 员工的被盗凭证。最初尝试使用这些凭据连接到 Uber 的网络失败,因为该帐户受 MFA 保护。为了克服这一安全障碍,黑客通过 What’s App 联系了 Uber 员工,并假装是 Uber 的安全人员,要求该员工批准将 MFA 通知发送到他们

    2024年02月04日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包