反调试——Windows异常-SEH

概念:

SEH:Structured Exception Handling

SEH是Windows默认的异常处理机制

如何使用

在代码中使用

__try


__except()//结构类型的语句

__except()小括号里面填写表达式,表达式为真的时候执行里面的内容

__try里面包含的是可能触发异常的语句,except里面包含的是出现了异常后执行的操作。

例子:

int main()
{
__try
{
cout<<"hello,world"<<endl;
}
__except(1)
{
cout<<"异常"<<endl;
}
return 0;
}

异常的作业

1 便于查找错误

2 可以用在反调试里面

异常处理机制

当我们在非调试状态下运行一个程序,程序如果触发了异常,会先判断是否有异常处理器,如果存在则跳转到异常处理函数去执行,如果不存在则退出程序

如果程序处于被调试状态,触发异常时,操作系统会先把异常抛给调试进程,也就是让调试器来处理异常。可以看到的现象就是触发了异常后,程序会暂停下来,也就是断下来,也就是断点的原理。当异常抛给调试器后,调试器可以选择:

1 修改触发异常的代码继续执行(程序会停在触发异常的代码处,导致异常的代码无法执行)

2 忽略异常交给SEH执行

也就是说Windows发生异常后的处理顺序为:1、调试器处理。2、SEH处理 3、崩溃

剖析SEH

SEH结构:

typedef struct _EXCEPTION_REGISTRATION_RECORD {
  struct _EXCEPTION_REGISTRATION_RECORD *Next;
  PEXCEPTION_ROUTINE Handler;//异常处理器,异常处理函数
} EXCEPTION_REGISTRATION_RECORD;

SEH在程序中实际上是以链表形式存在,每一个next都指向下一个SEH,这个链表也叫SEH链(如图):

每次添加一个异常就会添加该异常到异常链的头结点的位置

观察SEH是如何生成和使用的

生成两个程序观察,两个程序的区别就是一个有异常SEH,一个没有

//有异常的程序代码
#include<Windows.h>
#include<iostream>
int main()
{
__try
{
char* str = NULL;
str[0] = 'a';
}
__except(1)
{
printf("触发异常了\n");
}
printf("Sna1lGo\n");
return 0;
}
//没有异常的程序代码
#include<Windows.h>
#include<iostream>
int main()
{
char* str = NULL;
str[0] = 'a';
printf("Sna1lGo\n");
return 0;
}

分别生成后,都用od打开调试比对

然后都进入main函数

重点查看SEH的代码:

这四条指令就是SEH的关键代码,这一系列操作相当于创建了一个异常的结构体,然后添加到异常链的表头里面。

分析为什么是这样:

首先,大概画一个堆栈图:

先存进来了一个0x01293609,然后调用了fs:[0]这个东西,这个东西有点眼熟之前的TEP-PEB查找核心模块的时候有用过,但是这里,可以将fs:[0]直接理解为SEH链的表头。

这里取出来fs:[0]给eax后又把eax入栈,然后将fs:[0]赋值为esp

可以理解为在栈中创建了一个SEH变量,然后第一个字段存放了函数地址也就是0x01293690,next字段指向了fs:[0]也就是SEH链的第一个节点,然后再把fs:[0]的值修改为该节点的首地址,也就是让fs:[0]指向新的节点。(可能你的代码有点不一样,但是稍微分析下,对于异常处理这里也是一样的)

下面进入异常处理函数的地址查看(这里是0x01293690)

然后再运行会暂停下来,因为出现了异常,异常优先给调试器处理,但是调试器也是可以选择处理异常的:

在od的选项中选择调试选项,再选择异常:

就可以设置要捕获的异常类型了,选择忽略掉一些异常后再遇到就不会中断下来了。

也可以选择插件的StrongOD:

在取消掉Skip Some Exceptions之后,OD就会捕获所有异常了

SEH链存放的位置

经过上面的分析可以知道了,SEH存放的位置在fs:[0]这里

操作系统如何使用SEH

创建异常后,操作系统会自动把异常添加到头结点,然后把头指针指向新的异常节点

反调试——Windows异常-SEH的更多相关文章

  1. Windows反调试技术(下)

    OD的DBGHELP模块 检测DBGHELP模块,此模块是用来加载调试符号的,所以一般加载此模块的进程的进程就是调试器.绕过方法也很简单,将DBGHELP.DLL改名. #include <Wi ...

  2. Windows 下常见的反调试方法

    稍稍总结一下在Crack或Rervese中比较常见的一些反调试方法,实现起来也比较简单,之后有写的Demo源码参考,没有太大的难度. ①最简单也是最基础的,Windows提供的API接口:IsDebu ...

  3. Windows反调试技术(上)

    写在前面 在逆向工程中为了防止破解者调试软件,通常都会在软件中采用一些反调试技术来防破解.下面就是一些在逆向工程中常见的反调试技巧与示例. BeingDebuged 利用调试器加载程序时调试器会通过C ...

  4. Windows 32位-调试与反调试

    1.加载调试符号链接文件并放入d:/symbols目录下. 0:000> .sympath srv*d:\symbols*http://msdl.microsoft.com/download/s ...

  5. Windows 反调试技术——OpenProcess 权限过滤 - ObRegisterCallback

    转载: https://blog.xpnsec.com/anti-debug-openprocess/ 看雪翻译:https://bbs.pediy.com/thread-223857.htm 本周我 ...

  6. windows的SEH异常处理以及顶层异常处理

    前言 windows的SEH结构化异常处理是基于线程的,传统的SEH结构化异常会基于堆栈形成一条包含异常回调函数地址的链(SEH链).而fs:[0](TEB的第一个字段)指向这条链的链头,当有异常发生 ...

  7. 反调试技术常用API,用来对付检测od和自动退出程序

    在调试一些病毒程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己.为了了解如何破解反调试技术 ...

  8. WinDbg调试流程的学习及对TP反调试的探索

    基础知识推荐阅读<软件调试>的第十八章 内核调试引擎 我在里直接总结一下内核调试引擎的几个关键标志位,也是TP进行反调试检测的关键位. KdPitchDebugger : Boolean ...

  9. 基于TLS的反调试技术

    TLS(Thread Local Storage 线程局部存储) 一个进程中的每个线程在访问同一个线程局部存储时,访问到的都是独立的绑定于该线程的数据块.在PEB(进程环境块)中TLS存储槽共64个( ...

随机推荐

  1. 剑指 Offer 31. 栈的压入、弹出序列 + 入栈顺序和出栈顺序的匹配问题

    剑指 Offer 31. 栈的压入.弹出序列 Offer_31 题目详情: 解析: 这里需要使用一个栈来模仿入栈操作. package com.walegarrett.offer; /** * @Au ...

  2. Vulnhub dc-4靶机通关

    Vulnhub dc-4靶机通关 下载地址:https://download.vulnhub.com/dc/DC-4.zip 安装好dc-4靶机 使用局域网查看器扫描到ip地址 端口扫描,发现运行了8 ...

  3. Hibernate 组合查询+分页

    MVC模型:Hibernate+Struts2 dao层: public List<UserBean> searchList(UserBean uBean,int pageIndex,in ...

  4. nginx使用-2(模块和日志)

    默认官方模块 1.1.Gzip压缩 压缩文件,使文件变小,传输更快了.目前市场上大部分浏览器是支持GZIP的.IE6以下支持不好,会出现乱码情况. 官方文档:http://nginx.org/en/d ...

  5. 复制粘贴Ctrl+C改为自定义单键

    目录 那一瞬间不想用Ctrl+C了 (一)搜索渠道 关键词搜索 (二)方案对比 (三)最终方案Ditto 使用方法 (四)案例 1. 替换Ctrl+C快捷键: 2. 将英文小写替换为大写: 那一瞬间不 ...

  6. [Design Pattern With Go]设计模式-单例模式

    定义 一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例模式.当某些数据只需要在系统中保留一份的时候,可以选择使用单例模式. 饿汉式 饿汉式的实现方式比较简单.在类加 ...

  7. RabbitMQ 入门 (Go) - 3. 模拟传感器,生成数据并发布

    现在,我们需要模拟传感器,生成数据,并发布到 RabbitMQ. 建立传感器项目 在 GOPATH src 下建立文件夹 sensors,使用 go mod init 初始化,并创建 main.go. ...

  8. Prometheus时序数据库-报警的计算

    Prometheus时序数据库-报警的计算 在前面的文章中,笔者详细的阐述了Prometheus的数据插入存储查询等过程.但作为一个监控神器,报警计算功能是必不可少的.自然的Prometheus也提供 ...

  9. Redis 超详细自动管理Cluster集群工具上手 redis-trib.rb (多图,手把手)

    安装介绍 ​ redis-trib.rb是一款由Redis官方提供的集群管理工具,能够大量减少集群搭建的时间. ​ 除此之外,还能够简化集群的检查.槽迁徙.负载均衡等常见的运维操作,但是使用前必须要安 ...

  10. go的令牌桶实现库 go-rate

    关于我 我的博客|文章首发 go-rate是速率限制器库,基于 Token Bucket(令牌桶)算法实现. go-rate被用在LangTrend的生产中 用于遵守GitHub API速率限制. 速 ...