• Delphi


    技术交流,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

  • 相关阅读:
    使用npm安装一些包失败了的看过来(npm国内镜像介绍)(解决生成空的abp模板项目一直卡在还原cpm包中)
    .NET CORE 发布到IIS问题 HTTP ERROR 500.30
    .NET Core默认不支持GB2312,使用Encoding.GetEncoding(“GB2312”)的时候会抛出异常。
    .net c# 文件分片/断点续传之下载--客户端
    aspnetcore 实现断点续传
    C# 反射获取属性值、名称、类型以及集合的属性值、类型名称
    C# 3Des两种加密方式 (对应java中的desede/CBC/PKCS5Padding加密)
    Asp.NetCore3.1中多次读取Request.Body
    ASP.NET Core 2.0系列学习笔记-DI依赖注入
    C# Newtonsoft.Json JObject合并对象整理
  • 原文地址:https://www.cnblogs.com/findumars/p/5185237.html
Copyright © 2020-2023  润新知