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

【第42节】windows双机调试环境搭建和SEH原理剖析

目录

一、windows双机调试环境

1.1 双机调试是什么

1.2 准备工作

1.3 配置步骤

1.3.1 安装 VirtualKD

1.3.2 将target文件夹拷贝到虚拟机

1.3.3 在主机上使用vmmon64.exe监控虚拟机

二、SEH 原理剖析

2.1 TEB 与 FS 概述

2.2 手工注册 SEH


一、windows双机调试环境

1.1 双机调试是什么

        双机调试环境是一种通过两台计算机(主机与目标机)协同工作进行软件测试和问题排查的技术。在你的场景中,主机运行Windows 10,目标机为安装了Windows 7的VMware虚拟机,调试工具是WinDbg。以下是详细的配置步骤:

1.2 准备工作

虚拟机      vmware12
目标系统  win7-x32,确保已安装VMware 12及Windows 7(32位)作为目标系统
主机系统  win10
调试器      windbg,主机需安装Windbg及VirtualKD用于内核调试

1.3 配置步骤

1.3.1 安装 VirtualKD

在主机上解压并运行`virtualkd.exe`,选择目录完成安装。

安装后出现下面目录

1.3.2 将target文件夹拷贝到虚拟机

在虚拟机中运行程序

默认安装即可,系统会重新启动。

1.3.3 在主机上使用vmmon64.exe监控虚拟机

设置windbg的调试器路径

选择调试器

运行调试器

即可进行内核调试

注意事项
- 确保所有操作均以管理员权限执行,避免权限不足导致的问题。
- 如果遇到连接失败,检查COM端口设置或尝试重新配置串行端口。

        以上步骤将帮助你搭建一个稳定的双机调试环境,便于深入分析和解决系统级问题。

二、SEH 原理剖析

2.1 TEB 与 FS 概述

        大家都清楚,程序一旦产生异常,这个异常首先会被发送到内核层。而Windows系统所提供的异常处理机制,它肯定是在内核层把异常发出来以后,才会开始发挥作用去处理异常。

        Windows系统特别厉害,仅仅借助`_try`、`_except`这些关键字,就能够成功接管异常。而且在处理异常的过程中,程序还能正常运行,即便跳转来跳转去处理各种情况,也不会出现崩溃的状况。那这到底是怎么做到的呢?

        要弄明白这个问题,我们得从分析TEB和异常链入手。对于线程来说,在内核层有一个专门的数据结构,叫做ETHREAD;而在用户层,也有对应的一个数据结构,叫TEB 。

实验:建立双机调试,查看 TEB 结构

kd>dt TEB -b
nt!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x000 ExceptionList    : Ptr32
   +0x004 StackBase        : Ptr32
   +0x008 StackLimit       : Ptr32
   +0x00c SubSystemTib     : Ptr32
   +0x010 FiberData        : Uint4B
   +0x010 Version          : Uint4B
   +0x014 ArbitraryUserPointer : Ptr32
   +0x018 Self             : Ptr32
   +0x01c EnvironmentPointer : Ptr32
   +0x020 ClientId         : _CLIENT_ID
   +0x020 UniqueProcess    : Ptr32
   +0x024 UniqueThread     : Ptr32
   +0x028 ActiveRpcHandle  : Ptr32

        从这里能发现,TEB里的第一个字段是`NtTib` 。而在`NtTib`当中,它的第一个字段是`ExceptionList` 。这个`ExceptionList`其实就是一个链表头结点的地址。链表中的元素是按照下面这样来定义的: 

typedef struct _EXCEPTION_REGISTRATION_RECORD {
    struct _EXCEPTION_REGISTRATION_RECORD *Next;  // 指向下一个 SEH
    PEXCEPTION_ROUTINE Handler;                   // 异常处理函数
} EXCEPTION_REGISTRATION_RECORD;

        当异常出现的时候,系统会借助`FS:[0]`找到那条链表。然后,系统会按照顺序挨个调用链表里面的处理函数,用它们来处理异常。这,就是SEH的关键原理所在。至于微软提供的那些`_try`、`_except`等关键字,主要是为了让咱们用户在使用SEH机制的时候,感觉更方便、更顺手罢了 。 

2.2 手工注册 SEH

        搞懂SEH的大概运作方式之后,咱们就能自己动手注册SEH了。不过在这之前,得先知道SEH异常处理函数长啥样,也就是它的原型: 

NTAPI EXCEPTION_ROUTINE(
    _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
    _In_ PVOID EstablisherFrame,
    _Inout_ struct _CONTEXT *ContextRecord,
    _In_ PVOID DispatcherContext
);

手工注册代码如下:

#include "stdafx.h"
#include <windows.h>

EXCEPTION_DISPOSITION NTAPI seh(struct _EXCEPTION_RECORD *ExceptionRecord,PVOID EstablisherFrame,struct _CONTEXT *ContextRecord,PVOID DispatcherContext)
{
	printf("seh\n");
	// 继续执行
	return ExceptionContinueExecution;
}

int _tmain(int argc, _TCHAR* argv[])
{
//	EXCEPTION_REGISTRATION_RECORD node;
	/*
	  * 产生异常后 , 操作系统使用fs段寄存器找到TEB, 
	  * 通过TEB.ExceptionList 找到SEH链表的头节点, 
	  * 通过节点中记录的异常处理函数的地址调用该函数.
	*/
// 	node.Handler = seh;
// 	node.Next = NULL;

	_asm
	{
		push seh; // 将SEH异常处理函数的地址入栈
		push fs:[0];//将SEH头节点的地址入栈
		;// esp + 0 -- > [fs:0]; node.Next;
		;// esp + 4 -- > [seh]; node.handler;
		mov fs:[0], esp;// fs:[0] = &node;
	}


	*(int*)0 = 0;


	// 平衡栈空间
	// 还原FS:[0]原始的头节点
	_asm{
		pop fs : [0]; // 将栈顶的数据(原异常头节点的地址)恢复到FS:[0],然后再平衡4个字节的栈
		add esp, 4; // 平衡剩下的4字节的栈.
	}
	return 0;
}

SEH 机制的基本原理:

        当异常产生时,操作系统会通过 fs 段寄存器找到线程环境块(TEB),然后在 TEB 中通过 ExceptionList 找到 SEH 链表的头节点,最后根据头节点中记录的异常处理函数的地址来调用相应的异常处理函数。同时,注释中还提到了原本可以通过定义 EXCEPTION_REGISTRATION_RECORD 结构体变量 node 并设置其成员 Handler 为自定义的异常处理函数 seh,Next 为 NULL 来构建一个 SEH 节点。

上面的代码使用汇编语言手动安装 SEH 节点:

(1)push seh; 将自定义的 SEH 异常处理函数 seh 的地址压入栈中。

(2)push fs:[0]; 将当前 SEH 链表头节点的地址(存储在 fs:[0] 中)压入栈中。

(3)注释部分解释了栈中的数据布局,esp + 0 处是原来的 SEH 链表头节点地址(即新节点的 Next 指针所指向的位置),esp + 4 处是新的异常处理函数地址(即新节点的 Handler 成员)。

(4)mov fs:[0], esp; 将栈顶指针 esp 的值赋给 fs:[0],这样就把新的 SEH 节点插入到了 SEH 链表的头部,fs:[0] 现在指向了新创建的 SEH 节点。

触发异常:

        *(int*)0 = 0; 这行代码试图向内存地址 0 写入数据,由于地址 0 通常是受保护的,不能被随意访问和写入,所以这会触发一个异常,从而使系统调用刚刚安装的 SEH 异常处理函数。

异常处理完成后,需要恢复栈空间和原来的 SEH 链表:

(1)pop fs : [0]; 将栈顶的数据(即原来的 SEH 链表头节点地址)弹出并恢复到 fs:[0] 中,这样就还原了原来的 SEH 链表头节点。

(2)add esp, 4; 用于平衡栈空间,因为之前 push 操作使栈增加了 4 个字节(push seh; 操作压入了一个 4 字节的函数地址),这里通过增加 esp 的值来恢复栈的平衡。

        上面代码的主要目的是通过手动安装 SEH 节点来展示 Windows 系统中 SEH 机制的工作原理,包括如何注册自定义的异常处理函数,以及在异常处理完成后如何恢复系统的状态。

相关文章:

  • RadioMaster POCKET遥控器进入ExpressLRS界面一直显示Loading的问题解决方法
  • 【科普】轨道交通信号系统相关名词解释
  • 基础贪心算法集合2(10题)
  • flutter-Text等组件出现双层黄色下划线的问题
  • android-根据java文件一键生成dex文件脚本
  • js | 网页上的 json 数据怎么保存到本地表格中?
  • NAS-相关软件推荐——非相册和备份的
  • 影刀RPA证书题库包含初级、中级、高级和AP初级
  • Trinity三位一体开源程序是可解释的 AI 分析工具和 3D 可视化
  • try...catch、async/await和Promise区别与联系
  • Openlayers:实现聚合
  • [LeetCode 55] 跳跃游戏
  • 【今日三题】经此一役小红所向无敌(模拟) / 连续子数组最大和(动态规划) / 非对称之美(贪心)
  • 在Ubuntu下进行单片机开发是否需要关闭Secure Boot
  • 扩展欧几里得算法:求解乘法逆元
  • 【MySQL数据库】InnoDB存储引擎:逻辑存储结构、内存架构、磁盘架构
  • 门极驱动器DRV8353M设计(三)
  • OpenCV中的轮廓检测方法详解
  • OpenCV day2
  • 无人船 | 图解基于视线引导(LOS)的无人艇制导算法
  • 夜读丨一条鱼的使命
  • 主动权益基金一季度重仓股出炉:腾讯跃升至第一,阿里、比亚迪、中芯国际新进前十
  • 马上评丨超常设置战略急需专业,意味着什么
  • 我国将组织实施梦舟飞船零高度逃逸、揽月着陆器综合着陆起飞验证等试验
  • 龚正会见巴西里约热内卢州州长克劳迪奥·卡斯特罗
  • 同比增长1.2倍!一季度货物贸易项下跨境资金净流入2063亿美元