zoukankan      html  css  js  c++  java
  • windows终止处理程序( __try __finally) 简单解析

    本文大部分来自《windows核心编程》。

    例1

    //二话不说,直接上代码
    int
    Funcenstein2() { __try { return 3; } __finally { //在函数返回之前会处理__finally里的内容 cout<<"finally executed"<<endl; } return 4;//此函数返回3而不是4 }

    通过使用终止处理程序可以防止过早的执行return语句。当return语句试图退出try块的时候,编译器会让finally代码在它。即编译器保证finally代码块在出try块的时候return之前执行。

    者可以想知道,编译器是如何保证此功能的呢?原来当编译器检查程序代码时,会发现try代码里有一个return语句。于是,编译器就会生成一些代码先将返回值(例子中的 3)保存在一个由它创建的一个临时变量里,然后再执行finally语句块。这个过程被称之前为局部展开(LOCAL UNWIND)。更确切的说,当系统因try代码提前退出finally时就会发生局部展开。一旦finally代码块执行完毕,编译器所创建的临时变量值就会返回给函数调用者。

    由此可见,为了让整个机制运行起来,编译器必须生成一些额外的代码,而系统也要很执行一些额外的工作。在不同的cpu结构上,让终止处理工作起来的步骤也不同。需要注意的是,应该避免在try代码中使用return语句,因为这是对程序性能有害的。__leave关键字,它可以帮助我们发现那些有局部展开开销的代码

     例2

    int Funcenstein3()
    {
        //在try中使用goto语句时,就会产生局部展开以执行finally代码块。
        //这一次当finally执行完之后。因为try和finally中都没有函数返回语句,
        //所以ReturnValue标签后面的代码也会执行。因此这函数返回4。
        //但是由于代码破坏了try块到finally的正常执行流程,可能有比较大的性能损失,其程序取决于cpu体系结构。
    
        int ret=0;
        __try
        {
            ret=5;
            goto ReturnValue;
        }
        __finally
        {
            cout<<"finally executed"<<endl;
        }
    ReturnValue:
        return 4;
    }

     例3

    在这个例子中,终止处理将真正证明它的价值。首先看一下代码

    DWORD Funcfurter1()
    {
        DWORD dwTemp;
    
        //1. do any processing here
    
        __try
        {
            WaitForSingleObject(g_hSem,INFINITE);
            dwTemp=Funcinator(g_dwProtectedData);
        }
        __finally
        {
            ReleaseSemaphore(g_hSem,1,NULL);
        }
        return dwTemp;
    }

    假设try代码块中Funcinator函数存在一个缺陷会导致程序访问非法的内存。如果没有SEH这种情况下最络导致Windows错误报告(WER)弹出一个对话框:“Application has stopped working”。这个对话框在Windows上经常可以见到。一旦用户取消这个对话框,进程就会终止。但信号量依然被占用并再也得不到释放。其他进程中的纯种就会因为无休止的等待这个信号量而得不到CPU时间片。如果把信号量放在finally之中,即使用try中调用的函数发生了内存访问违规这样的异常,这个信号量也可以被释放。但是,请注意,从Windows Vista开始,须显式地保护try/finally框架,以确保在异常抛出时,finally代码会执行。

     例4

    现在不防做一个实验,判断一下这个函数的返回值

    DWORD FuncalDoodLeDoo()
    {
        DWORD dwTemp=0;
    
        while (dwTemp<10)
        {
            __try
            {
                if(dwTemp==2)
                    continue;
                if(dwTemp==3)
                    break;
            }
            __finally
            {
                dwTemp++;
            }
            dwTemp++;
        }
        dwTemp+=10;
        return dwTemp;
    }

    让我们逐步分析这个函数执行的过程:一开始将dwTemp赋值为0,然后try块中的代码开始执行,但是两个if语句都为false。于是程序正常进入到finally代码块,在这里给dwTemp的值上加1,而finally块后面的代码又将dwTemp加1。

    下一次循环开时,dwTemp=2,第一个if为true,程序试如果没有__finally程序会跳到while条件判断处执行,但dwTemp值班不会改变,这将会是一个死循环。但是现在我们有终止处理程序,系统注意到continue语句将会导致提前跳出try块,于是强制执行finally语句块。 在finally语句块中dwTemp被增加到3.这次finally之后的代码块没有机会执行。因此finally执行完之后程序跳到循环顶部执行。

    现在我们分析第三次迭代,这次第一个if判断表达式的值为false,第二个表达式的值为true,系统再次侦测到程序流想要提前跳出try块,于是调用finally代码块,这里的dwTemp增加到4.因为break语句的执行程序控制流从whhile循环后开始继续。因而finally块之后循环以内的代码就不会被执行了。而循环之后的代码将dwTemp的值设置为14。这是程序最终返回的结果。不用我指明,请教也不会写出FuncalDoodleDoo这样的代码。此处只是为了演示终止处理程序是如何工作的。‘

    绝大多数部情况下,try块中的提前退出都会被终止处理程序所捕获,但是在进程或线程提前终止的情况下,系统没法保证finally代码块的执行。调用ExitThread或者ExitProcess可以马上终止纯种或进程,而不会引发finally执行。同样如果当前纯种或者进程因为另一个程序调用TerminateThread或TerminateProcess而不得不结束,finally代码块也不会被执行,有一些c运行期函数比如(abort),因为在其内部最络调用的是ExitProcess,也会导致finally块不能执行。我们没法阻止别的线程杀死我们的线程或进程,但是可以在自己的代码中尽量避免对ExitThread或ExitProces的草率调用。

    例5

    DWORD Funcenstein4()
    {
        DWORD dwTemp;
        
        //1. do any processing here    
        __try
        {
            WaitForSingleObject(g_hSem,INFINITE);
            
            g_dwProcectedData=5;
            dwTemp=g_dwProcectedData;
            //return the new value
            return dwTemp; 
        }
        __finally
        {
            ReleaseSemaphore(g_hSem,1,NULL);
            return 103;
        }
        dwTemp = 9;
        return dwTemp;
    }

    在上面的函数中,try中的代码试图用return 返回给调用者。正如我们前面提到的那样,试图在try块中提前退出函数导致编程器生成一些额外代码,将函数返回结果保存在一个临时变量中,然后执行finally中又多了一个return,那么导致103被写入到编译器生成的临时变量时。从而覆盖了原先的值5。而返回103

    本人新博客网址为:http://www.hizds.com
    本博客注有“转”字样的为转载文章,其余为本人原创文章,转载请务必注明出处或保存此段。c++/lua/windows逆向交流群:69148232
  • 相关阅读:
    dfs
    spring框架(3)— spring集合类的注入
    android入门 — Service
    android入门 — 多线程(一)
    android入门 — Activity启动模式
    android入门 — Activity生命周期
    android入门 — ProgressDialog/DatePickerDialog/TimePickerDialog
    Numpy入门
    Numpy入门
    Numpy入门
  • 原文地址:https://www.cnblogs.com/zhangdongsheng/p/2618957.html
Copyright © 2011-2022 走看看