• 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
  • 相关阅读:
    10. Regular Expression Matching
    9. Palindrome Number
    6. ZigZag Conversion
    5. Longest Palindromic Substring
    4. Median of Two Sorted Arrays
    3. Longest Substring Without Repeating Characters
    2. Add Two Numbers
    链式表的按序号查找
    可持久化线段树——区间更新hdu4348
    主席树——树链上第k大spoj COT
  • 原文地址:https://www.cnblogs.com/zhangdongsheng/p/2618957.html
Copyright © 2020-2023  润新知