Windows x64为动态代码添加unwind信息

  • 0x00
  • 0x01

0x00

64位程序的堆栈回溯与32位差别很大,调试器和 RtlCaptureStackBackTrace 无法再使用 ebp 链来遍历整个堆栈,而只能使用 prolog信息来回溯堆栈。正常的C/C++函数的prolog信息在编译时候生成并记录在PE64文件的unwind信息中,另外当前函数对应的异常处理函数也添加在unwind信息的尾部。所以对于不存在unwind信息的函数,异常处理流程无法找到其异常处理函数,也很可能无法正常展开堆栈找到其调用者,从而找到上层的异常处理函数,从而跳过SEH的处理链,导致抓取异常失败。最终结果是程序崩溃了,但是没有留下任何痕迹。这里之所以说很可能,是因为x64的堆栈做展开时还是有一定容错性的,具体可以参看之前的文章: windows x64异常分发流程
因此对于很多手写汇编,会发现崩溃但是不能正常生成dump的情况,这样对查BUG影响很大。
当然对手写汇编的unwind 信息的添加还是比较简单的,以 MASM汇编器为例,可以通过添加 .PUSHREG, .ALLOCSTACK 来告诉汇编器对应的堆栈操作,具体可见 Directives Reference。当然在手写汇编的过程中,一定要遵循x64 prolog规范,否则展开还是会出错,具体可以参考 x64 software conventions。
对于编译好的函数,我们可以在windbg中使用 .fnent 命令查看其unwind信息。以kernelbase!ReadFile为例,以下是其unwind信息:

0x01

对于动态生成的代码,我们无法使用手写汇编的方法生成unwind信息。这里有两个方法可以比较容易地实现unwind信息的生成。
第一种就是依照 x64 exception handling的描述,手动构造结构体。一般来说,只要不是太复杂的prolog,都比较简单可以实现。
另一种方法比较讨巧,很多时候我们生成的动态代码都有一个基础的汇编代码在。动态代码的构造首先从基础的汇编代码复制而来,之后修改其中的几个变量,从而形成新的代码。这种一般在hook引擎中使用的比较多。对于这种情况,我们可以根据第一节内容为基础汇编添加unwind信息,之后再拷贝给动态代码使用。
以下是为 easyhook 动态代码添加的unwind信息的代码:

typedef union _UNWIND_CODE {struct {UCHAR CodeOffset;UCHAR UnwindOp : 4;UCHAR OpInfo : 4;};USHORT FrameOffset;
} UNWIND_CODE, * PUNWIND_CODE;typedef struct _UNWIND_INFO {UCHAR Version : 3;UCHAR Flags : 5;UCHAR SizeOfProlog;UCHAR CountOfCodes;UCHAR FrameRegister : 4;UCHAR FrameOffset : 4;// For alignment purposes, this array always has an even number of entries, and the final entry is potentially unusedUNWIND_CODE UnwindCode[1];
} UNWIND_INFO, * PUNWIND_INFO;static void StopMe() {}static EXCEPTION_DISPOSITION JustContinueSearch(PEXCEPTION_RECORD ExceptionRecord, ULONG64 EstablisherFrame, PCONTEXT ContextRecord, PDISPATCHER_CONTEXT DispatcherContext) {return ExceptionContinueSearch;
}static void StopMeToo() {}UCHAR GetUnwindCodeArrayCnt(UCHAR uc)
{return ((uc + 1) >> 1) << 1;;
}ULONG GetUnwindInfoLength(DWORD64 dwImageBase,PRUNTIME_FUNCTION pFuncEntry)
{if (!pFuncEntry) return 0;PUNWIND_INFO pInfo = (PUNWIND_INFO)(dwImageBase + pFuncEntry->UnwindInfoAddress);ULONG ulSum = FIELD_OFFSET(UNWIND_INFO, UnwindCode);__try {UCHAR cnt = GetUnwindCodeArrayCnt(pInfo->CountOfCodes);ulSum += cnt * sizeof(UNWIND_CODE);ulSum += sizeof(ULONG) * 2;}__except (EXCEPTION_EXECUTE_HANDLER) {return 0;}return ulSum;
}ULONG GetExceptionFuncLength()
{auto dwSM = (DWORD64)StopMe;auto dwSM2 = (DWORD64)StopMeToo;auto excpFunc = (DWORD64)JustContinueSearch;DWORD dwDiff = 0;if (dwSM > excpFunc) {dwDiff = dwSM - excpFunc;}else if (dwSM2 > excpFunc) {dwDiff = dwSM2 - excpFunc;}if (!dwDiff || (dwDiff > 0x20)) {return 0x20;}return dwDiff;
}void LhBuildUnwindInfoForTrampoline(LOCAL_HOOK_INFO* pHook,unsigned char* uc,int nSize)
{#ifndef _WIN64return;
#endifif (!uc || !nSize) return;if (!pHook || !pHook->Trampoline) {return;}DWORD64 dwImageBase = 0;auto pOriFuncEnt = RtlLookupFunctionEntry((ULONG_PTR)GetTrampolinePtr(), &dwImageBase, nullptr);if (!pOriFuncEnt) {return;}DWORD64 dwPreImageBase = 0;auto pFuncEntry = RtlLookupFunctionEntry((ULONG_PTR)pHook->Trampoline, &dwPreImageBase, nullptr);if (pFuncEntry) {RtlDeleteFunctionTable(pFuncEntry);}auto nLen = GetUnwindInfoLength(dwImageBase,pOriFuncEnt);if (!nLen) return;//ImageBase is pHookauto nTotalSize = sizeof(RUNTIME_FUNCTION) + nLen + GetExceptionFuncLength() + 10;//padding 10 bytes toif (nTotalSize > nSize) return;memset(uc, 0, nTotalSize);PRUNTIME_FUNCTION pRF = (PRUNTIME_FUNCTION)uc;PUNWIND_INFO pInfo = (PUNWIND_INFO)(pRF + 1);pRF->BeginAddress = (DWORD64)pHook->Trampoline - (DWORD64)pHook;pRF->EndAddress = pRF->BeginAddress + GetTrampolineSize();pRF->UnwindInfoAddress = (DWORD64)pInfo - (DWORD64)pHook;PUNWIND_INFO pOriUnwindInfo = (PUNWIND_INFO)(dwImageBase + pOriFuncEnt->UnwindInfoAddress);memcpy(pInfo, pOriUnwindInfo, FIELD_OFFSET(UNWIND_INFO, UnwindCode) + pOriUnwindInfo->CountOfCodes * sizeof(UNWIND_CODE));auto nIdx = FIELD_OFFSET(UNWIND_INFO, UnwindCode) + GetUnwindCodeArrayCnt(pOriUnwindInfo->CountOfCodes) * sizeof(UNWIND_CODE);auto pHandlerVA = (ULONG*)((unsigned char*)pInfo + nIdx);auto pExceptFunc = pHandlerVA + 2;*pHandlerVA = (ULONG)((DWORD64)pExceptFunc - (DWORD64)pHook);*(pHandlerVA + 1) = 1;memcpy(pExceptFunc, JustContinueSearch, GetExceptionFuncLength());pInfo->Flags |= UNW_FLAG_EHANDLER;RtlAddFunctionTable(pRF, 1, (DWORD64)pHook);
}

对以上代码有几个点需要解释下:

  1. 从第一节列出的文档中可以知道,unwind信息是包含在RUNTIME_FUNCTION之中的,所以我们也要填充RUNTIME_FUNCTION结构;
  2. RUNTIME_FUNCTION 与 unwind信息中的地址都是VA与ImageBase的差值,并且差值要可以用DWORD表示。其中ImageBase我们可以随便定,只要其小于所有我们需要索引的结构的地址即可,并且在 RtlAddFunctionTable 第三个参数中传入ImageBase地址。由此,我们只要分配足够容纳动态代码,RUNTIME_FUNCTION ,unwind信息等的Buffer,并将其首地址作为ImageBase使用即可。LhBuildUnwindInfoForTrampoline中第一个参数就是这个作用;
  3. LhBuildUnwindInfoForTrampoline中的第二个参数是存放RUNTIME_FUNCTION与unwind信息的起始地址,其在ImageBase代表的buffer范围之内;第三个参数是uc可用的最大的长度;
  4. GetTrampolinePtr()返回手写汇编的地址,对其调用 RtlLookupFunctionEntry 可以获取其 RUNTIME_FUNCTION 信息,从而可以将其拷贝到我们构造的 RUNTIME_FUNCTION 中;
  5. GetUnwindCodeArrayCnt返回值都是偶数,这个是prolog规范要求的,具体可以见第一节的链接。

Windows x64为动态代码添加unwind信息相关推荐

  1. php批量给文件添加头部版权,给代码添加版权信息

    版权声明:本文系作者原创.未经许可,不得转载. 以前写的代码没有加上版权信息.后来要全部添加,一个一个添加当然很慢,于是写了一个脚本自动添加. 基本思路: 1.列出目录下所有文件 2.得到后缀名,根据 ...

  2. 【Windows 逆向】OD 调试器工具 ( 分析 OD 硬件断点处的关键代码 | 添加硬件断点 | 关键代码 | MOV 指令 | EAX 寄存器值分析 | 使用命令查看 esi+0cc 地址 )

    文章目录 一.添加硬件断点 二.关键代码 三.MOV 汇编指令格式 四.EAX 寄存器值分析 五.使用命令查看 esi+0cc 地址 一.添加硬件断点 在上一篇博客中 , 在子弹个数数据内存地址 07 ...

  3. Windows phone listbox动态添加列表项

    当我们想做一个播放器的个人列表管理时可能需要动态的添加的一些"文件夹",这时可能不是太清楚如何动态的进行添加. 首先我们我们先来看下前台: 1 <Grid x:Name=&q ...

  4. C# 动态添加SEO 信息,不和静态页面重复和叠加

    动态添加SEO 信息,不和静态页面重复和叠加,就一个方法,用到了做个记录,以后直接用就OK了,需要的同学也可以直接拿去用. 1 /// <summary> 2 /// 动态设置 SEO 信 ...

  5. 如何将自己的代码自动添加版权信息[转]

    现在大多数公司都规定程序员在程序文件的头部加上版权信息,这样每个人写的文件都可以区分开来,如果某个文件出现问题就可以快速的找到文件的创建人,用最短的时间来解决问题,常常是以下格式: //======= ...

  6. 从C#中通过Windows窗体添加信息到数据库 (添加学生信息)

    如上图所示界面,当我们点击保存按钮时将会将表格中的数据保存到数据库中去,与数据库进行一个交互 第一步我们就是要获取到表格中的数据 string pwd = textpwd.Text; //获得第一次输 ...

  7. java添加员工代码_1.7.5 添加员工信息实现过程(1)

    1.7.5  添加员工信息实现过程(1) 当用户单击如图1.16所示的员工信息窗体的"添加"按钮后,将弹出添加员工信息窗体.添加员工信息由两部分组成,分别为添加员工基本信息与添加员 ...

  8. jq 在元素中追加html代码,jquery添加html代码,怎样动态的添加一条html代码?

    下面的文章内容要给大家介绍的就是jquery动态的添加html代码的内容,那么你知道应该如何添加吗?一起来看看方式吧. 添加新bai内容的4个jquery方法,分别是append() - 在被bai选 ...

  9. postman 不安全网站_Postman所有版本下载_Postman for windows x64 mac下载

    Postman背景介绍 用户在开发或者调试网络程序或者是网页B/S模式的程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的Firebug等网页调试工具.今天给大家介绍的 ...

最新文章

  1. 目标检测特殊层:ROI Align层详解
  2. logback基础配置文件
  3. cad高程如何提取到cass软件_建筑CAD软件中如何设置墙厚标注? - 数码区
  4. 2021年中国一次性弹性泵市场趋势报告、技术动态创新及2027年市场预测
  5. 编程中什么叫做元素什么叫帧_R编程中的数据帧
  6. dubbo k8s 服务发现_记一次dubbo服务发现导致的OOM
  7. 客户说发货慢怎么回复_买家投诉卖家物流送货慢怎么处理
  8. DHCP报文单播/广播分析
  9. Hbase的table存储详解
  10. SIFT特征点的匹配正确率衡量标准与量化
  11. ArchLinux安装fcitx5-rime
  12. SAP系统架构和技术平台
  13. 使用终端生成icns,icon文件
  14. 2021-2022学年广州市育才实验学校九年级第一学期期中考试英语试题
  15. 灵山卫大集逢一逢六开集。也就是阴历的日期,如果末尾是1或6,例如,阴历二月十六,就是赶灵山卫大集的日子。下面,请根据输入的阴历日期,判断这一天是否赶集吧。
  16. 解决Could not resolve archetype org.apache.maven.archetypes:maven-archetype-quickstart
  17. 马斯克称“很快”将会提高纯视觉Autopilot系统最高限速
  18. 软件项目管理==风险计划
  19. HW | 安装RTXA6000的注意事项 driver不匹配PKCS#7 signature not signed with a trusted key的修复
  20. 【caffe】caffe结构解析(一)

热门文章

  1. 对声卡输出进行录音的设置.
  2. c语言编译器下载 天天,简单C语言编译器(编译原理)
  3. 如何用计算机装手机系统,如何通过一键操作手机重新安装计算机系统?
  4. uc云观媒体服务平台_UC云观自媒体为什么没有阅读量?
  5. 《Dotnet9》系列-开源C# WPF控件库2《Panuon.UI.Silver》强力推荐
  6. python抓取百度地图数据_Python 百度地图爬虫 - 搜索数据采集
  7. 16-命令模式Quarkus实现
  8. leetcode765-情侣牵手
  9. 艾司博讯:拼多多参加活动的价格怎么改
  10. MQTT协议使用总结