技术交流,DH讲解.

前几天一个朋友在弄游戏外挂想带NP调试,就像自己来捕获游戏的异常.
好像就要用到SEH这方面的知识.
一起研究了一下,这里看下研究 和 在网上找的资料吧.
SEH就是Structure Exception Handling.结构化异常处理,具体可以看下MSDN.
MSDN在手,走遍天下无敌手.哈哈.
当时先自己看下Delphi 怎么实现try..except..end的吧.我们写段程序然后调试就知道了.

1
2
3
4
5
6
7
8
9
10
11
Procedure TForm1.Button1Click( Sender: TObject );
Var
    a: TForm1;
Begin
    a := Nil;
    Try
        a.Show;
    Except
        ShowMessage( '1111' );
    End;
End;


这样是会发生异常的吧,我们下断点,然后调试的时候ctrl + alt + c进入CPU窗口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Unit1.pas.38: a := Nil;
00452702 33C0 xor eax,eax
Unit1.pas.39: Try
00452704 33D2 xor edx,edx
00452706 55 push ebp
00452707 6821274500 push $00452721
0045270C 64FF32 push dword ptr fs:[edx]
0045270F 648922 mov fs:[edx],esp
Unit1.pas.40: a.Show;
00452712 E811B3FFFF call TCustomForm.Show
00452717 33C0 xor eax,eax
00452719 5A pop edx
0045271A 59 pop ecx
0045271B 59 pop ecx
0045271C 648910 mov fs:[eax],edx
0045271F EB14 jmp +$14
00452721 E91E12FBFF jmp @HandleAnyException
Unit1.pas.42: ShowMessage( '1111' );
00452726 B844274500 mov eax,$00452744
0045272B E85C8DFDFF call ShowMessage
00452730 E87715FBFF call @DoneExcept
Unit1.pas.44: End;

我们看见了try模块的代码了吧.好的自己来写个函数.

1
2
3
4
5
6
7
8
Procedure SetExceptionProc( Proc: Pointer );
Asm
    //将回调函数指针压入堆栈
    push eax
    //保护 原来的处理函数
    push fs:[0]
    mov fs:[0],esp
End;

你看和Delphi代码里面Try反编译出来的一样吧.
至于为什么要这样写?我也不知道,所以我们现在需要去找资料了.
发生异常时系统的处理顺序(by Jeremy Gordon): 
   1.系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正在被调试,则系统 
   挂起程序并向调试器发送EXCEPTION_DEBUG_EVENT消息.呵呵,这不是正好可以用来探测调试器的存在吗? 
   2.如果你的程序没有被调试或者调试器未能处理异常,系统就会继续查找你是否安装了线程相关的异常处理例程,如果 
   你安装了线程相关的异常处理例程,系统就把异常发送给你的程序seh处理例程,交由其处理. 
   3.每个线程相关的异常处理例程可以处理或者不处理这个异常,如果他不处理并且安装了多个线程相关的异常处理例程, 
可交由链起来的其他例程处理. 
   4.如果这些例程均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知debugger. 
   5.如果程序未处于被调试状态或者debugger没有能够处理,并且你调用SetUnhandledExceptionFilter安装了最后异 
   常处理例程的话,系统转向对它的调用. 
   6.如果你没有安装最后异常处理例程或者他没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框, 
   你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统 
   就调用ExitProcess终结程序. 
   7.不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开,这是线程异常处理例程最后清理的机会. 
事实上,当异常发生时,系统给了我们一个处理异常的机会,他首先会调用我们自定义的seh处理例程,当然也包括 
了相关信息,在调用之前,系统把包含这些信息结构的指针压入stack,供我们的异常处理例程调用, 
传递给例程的参数通常是四个,其中只有三个有明确意义,另一个到现在为止还没有发现有什么作用, 
这些参数是:pExcept:DWORD,pErr:DWORD,pContext:DWORD,pDispatch意义如下: 
pExcept: --- EXCEPTION_RECORD结构的指针 
pErr: --- 前面ERR结构的指针 
pContext: --- CONTEXT结构的指针 ,里面都是我们寄存器的值.
pDispatch:---没有发现有啥意义
Delphi里面已经定义好了这些结构体指针了,我就不多说了.
我们来把回调函数写出来吧.

1
2
3
4
5
Function ExceptionProc( pExcept: PExceptionRecord;pError: Pointer;pContxt: PContext;pDispatch: Pointer ): Integer; Stdcall;
Begin
    ShowMessage( '1111' );
    Result:=0;
End;

注意是stdcall调用方式,貌似有些资料上面是cdecl.这里我们先不管这么多了.
现在我们看看系统是怎么调用我们回调函数的.
关键的 Win32 数据结构——线程信息块(即 TEB 和 TIB)。
该数据结构的某些域在 Windows NT、Windows 95、Win32s 和 OS/2 平台上是一样的。
TIB 中的第一个 DWORD 是指向线程 EXCEPTION_REGISTRATION 结构的指针。
在 Intel Win32 平台上,FS 寄存器总是指向当前的 TIB。
因此,在 FS:[0]位置,你能找到 EXCEPTION_REGISTRATION 结构的指针。
这里也就解释了 我们SetExceptionProc函数了.
好的我们自己写段代码来测试一下了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Procedure TestException( );
Begin
    //try
    SetExceptionProc( @ExceptionProc );
    //make a exception
    Asm
        xor edx,edx
        mov [edx],0
    End;
    //恢复异常
    Asm
        pop eax
        mov fs:[0],eax
        add esp,8
    End;
End;
Procedure TForm1.Button2Click( Sender: TObject );
Begin
    TestException;
End;

测试中恢复异常还是有问题.

终于在CSDN的书呆子的博客上面找到了答案.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
Program Project2;
{$APPTYPE CONSOLE}
Uses
    SysUtils,
    Windows;
Type
    PExecption_Handler= ^Exception_Handler;
    PException_Registration= ^Exception_Registration;
    _ExceptionHandler= Record
        ExceptionRecord: PExceptionRecord;
        SEH: PException_Registration;
        Context: PContext;
        DispatcherContext: Pointer;
    End;
    Exception_Handler= _ExceptionHandler;
    _ExceptionRegistration= Record
        Prev: PException_Registration;
        Handler: PExecption_Handler;
    End;
    Exception_Registration= _ExceptionRegistration;
Const
    EXCEPTION_CONTINUE_EXECUTION= 0; ///恢复CONTEXT里的寄存器环境,继续执行
    EXCEPTION_CONTINUE_SEARCH= 1; ///拒绝处理这个异常,请调用下个异常处理函数
    EXCEPTION_NESTED_EXCEPTION= 2; ///函数中出发了新的异常
    EXCEPTION_COLLIDED_UNWIND= 3; ///发生了嵌套展开操作
    EH_NONE= 0;
    EH_NONCONTINUABLE= 1;
    EH_UNWINDING= 2;
    EH_EXIT_UNWIND= 4;
    EH_STACK_INVALID= 8;
    EH_NESTED_CALL= 16;
    STATUS_ACCESS_VIOLATION= $C0000005; ///访问非法地址
    STATUS_ARRAY_BOUNDS_EXCEEDED= $C000008C;
    STATUS_FLOAT_DENORMAL_OPERAND= $C000008D;
    STATUS_FLOAT_DIVIDE_BY_ZERO= $C000008E;
    STATUS_FLOAT_INEXACT_RESULT= $C000008F;
    STATUS_FLOAT_INVALID_OPERATION= $C0000090;
    STATUS_FLOAT_OVERFLOW= $C0000091;
    STATUS_FLOAT_STACK_CHECK= $C0000092;
    STATUS_FLOAT_UNDERFLOW= $C0000093;
    STATUS_INTEGER_DIVIDE_BY_ZERO= $C0000094; ///除0错误
    STATUS_INTEGER_OVERFLOW= $C0000095;
    STATUS_PRIVILEGED_INSTRUCTION= $C0000096;
    STATUS_STACK_OVERFLOW= $C00000FD;
    STATUS_CONTROL_C_EXIT= $C000013A;
Var
    G_TEST: DWORD;
Procedure Log( LogMsg: String );
Begin
    Writeln( LogMsg );
End;
//看这个回调函数,和我们那个有点儿区别,第二个参数的作用原来是ExceptionRegistration,原来秘密在它身上
Function ExceptionHandler( ExceptionHandler: EXCEPTION_HANDLER ): LongInt; Cdecl;
Begin
    Result := EXCEPTION_CONTINUE_SEARCH;
    If ExceptionHandler.ExceptionRecord.ExceptionFlags= EH_NONE Then
    Begin
        Case ExceptionHandler.ExceptionRecord.ExceptionCode Of
            STATUS_ACCESS_VIOLATION:
            Begin
                Log( '发现异常为非法内存访问,尝试修复EBX,继续执行' );
                ExceptionHandler.Context.Ebx := DWORD( @G_TEST );
                Result := EXCEPTION_CONTINUE_EXECUTION;
            End;
            Else
                Log( '这个异常我无法处理,请让别人处理吧' );
            End;
    End
    Else
    If ExceptionHandler.ExceptionRecord.ExceptionFlags= EH_UNWINDING Then
        Log( '异常展开操作' );
    End;
 
Begin
    Asm
    ///设置SEH
        XOR EAX, EAX
        PUSH OFFSET ExceptionHandler
        PUSH FS:[EAX]
        MOV FS:[EAX], ESP
    ///产生内存访问错误
        XOR EBX, EBX
        MOV [EBX], 0
    ///取消SEH
        XOR EAX, EAX
    //这里用的这个 而不是我们用的那个pop eax呀..哈哈.一切正常了
        MOV ECX, [ESP]
        MOV FS:[EAX], ECX
        ADD ESP, 8
    End;
    Readln;
End.

牛人拜读了.大家可以去CSDN上面看下.

好了我是DH.大家想了解更多可以看看雪的加密解密 以及 那个 什么软件漏洞分析 书上面都有SEH的章节.

http://www.cnblogs.com/huangjacky/archive/2009/12/09/1620644.html

Delphi - SEH研究的更多相关文章

  1. delphi VCL研究之消息分发机制-delphi高手突破读书笔记

    1.VCL 概貌 先看一下VCL类图的主要分支,如图4.1所示.在图中可以看到,TObject是VCL的祖先类,这也是Object Pascal语言所规定的.但实际上,TObject以及TObject ...

  2. Delphi窗体研究,留个爪,以后回来研究

    Delphi - 窗体创建过程   来自大富翁. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ...

  3. DELPHI XE5

    一直觉得DELPHI7之后,如果写WINDOWS桌面应用,没什么变化. 一直在痛苦,为什么DELPHI提供的控件那么丑陋,透明等什么功能都做的那么差. 一直在郁闷,新装的DELPHI都不带DEMO. ...

  4. 用Delphi直接获取bmp图片的像素

    用Delphi直接获取bmp图片的像素,并存储显示出.(此像素主要用在LED上显示).希望高手能给出代码啊!! function getImagePixels(f: string): Integer; ...

  5. TWinControl的消息覆盖函数大全(41个WM_函数和31个CM_函数,它的WndProc就处理鼠标(转发)、键盘(取消拖动)、焦点、和WM_NCHITTEST一共4类消息)

    注意,这些函数只有Private一种形式(也就是不允许覆盖,但仍在动态表格中): 其中TWinControl对TControl有10个消息进行了覆盖(红色标记),其中有2个是WM_消息,8个是CM_消 ...

  6. 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  7. Delphi 中 COM 实现研究手记(一)

    前言 前些日子用 Delphi 写了一个 Windows 外壳扩展程序,大家知道 Windows 外壳扩展实际上就是 COM 的一种应用 -- Shell COM,虽然整个程序写得还算比较顺利,但写完 ...

  8. 关于Delphi XE2的FMX的一点点研究之消息篇

    Delphi XE2出来了一阵子了,里面比较抢眼的东西,除了VCLStyle这个换肤的东西之外,另外最让人眼亮的应该是FMX这个东西了.万一的博客上都连载了一票的关于FMX的使用心得了.我还是没咋去关 ...

  9. TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)

    在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供.   花了两天的事件研究了 ...

随机推荐

  1. idea 使用问题总结

    tomcat     edit configurations配置问题:         在deployment选项卡内增加artifact到server,在Application context选择应 ...

  2. APUE学习笔记——10.15 sigsetjmp和siglongjmp

    转载自:sigsetjmp使用方法 如侵犯您的权益,请联系:windeal12@qq.com sigsetjmp使用方法 分类: c/c++ linux2012-02-03 12:33 1252人阅读 ...

  3. iOS UIView非常用方法及属性详解

    在调用视图的 Quartz  调用都可以正确地在视图中描画.   视图对象通过 bounds .和 center  属 性声明来跟踪自己的大小和位置.frame 属性包含一个矩形,即边框矩形,用于指定 ...

  4. stm32之RCC

    stm32时钟系统的意义: 1.电源的开关作用,达到低功耗效果: 2.调节时钟的速度: 对于每个外设,都要设置设置,stm32的时钟系统为了更低功耗: STM32时钟系统框图分析: 时钟源: 时钟是S ...

  5. iOS创建本地通知和删除对应的通知,工作日通知

    本文的代码主要是:创建本地通知,删除对应的本地通知,创建工作日闹钟 直接上代码: // // ViewController.m // LocalNSNotification // // Created ...

  6. E - Phone List(字典序,string类型使用)

    Description Given a list of phone numbers, determine if it is consistent in the sense that no number ...

  7. CentOS下安装两个或多个Tomcat7

    链接地址:http://lcbk.net/tomcat/1407.html 首先安装JDK 安装之前检查下是否已经安装了openJDK,如果已安装,建议用yum remove 卸载掉. [root@b ...

  8. GitHub的问题

    出现failed to publish the branch, 转自:http://blog.csdn.net/cucmakeit/article/details/29407329 (windows系 ...

  9. CodeIgniter 应用开发笔记 - 3

    使用migration建数据表 一.新建migrations文件夹 在application新建一个文件夹migrations,存放建表类. 建表类使用用户手册中的代码作为模板(user_guide/ ...

  10. ant—学习记录一

    <?xml version="1.0"?> <project name="helloWorld"> <target name=&q ...