首页
社区
课程
招聘
[原创]文件句柄分配的原理探测(三)---全面深入
发表于: 2005-4-22 22:10 15053

[原创]文件句柄分配的原理探测(三)---全面深入

2005-4-22 22:10
15053

看过我上一篇文章《深入VC流》的朋友想必对VC流系统的认识有了一定基础,文章的着重点在于流类库的两大基类,关于streambuf类和ios类及其常用的派生类iostream,ostream,iostream从原理和实验两方面已经介绍的差不多了,而对streambuf类的派生类以及文件流类fstream,ifstream,ofstream并没有涉及。这篇文章就是在此基础上使对VC流的认识更上一层楼,着重介绍文件流相关的原理并在此基础上通过反汇编逐渐靠近目标--透视文件句柄分配的原理。

    在文章的开始,我还是依照自己的惯例介绍一下在调试时会涉及到内容的基本原理。

一。原理篇

1。filebuf类:streambuf类的派生类,其结构原理可以参见《深入VC流》这里不再赘述。其本质也是系统在配置缓冲区处理的时候划分一块固定的内存区域--保留区域,只不过是属于文件流专用。

先说明一点:既然“保留区域”是由streambuf对象配置的,那么“文件保留区域”就是filebuf对象配置的。总之,清楚“保留区域是由其所对应的对象所配置的”就可以了。在这里强调这一点是因为当我调试进行到尾声的时候才发现自己一直把对象与保留区域混为一谈,例如rdbuf( )函数返回的是对象指针而不是保留区域指针。关于这一点在《深入VC流》中也忽略了。切记!!!

filebuf类也有属于自己的成员变量及相关的函数:

1)。x_fd:文件描述符,用于标识文件保留区域是否于文件绑定;

2)。x_fOpened:

3)。filebuf();构造空的文件保留区域(实际由系统分配)

          filebuf( filedesc fd );构造文件保留区域并且绑定到指定的文件

          filebuf( filedesc fd, char* pr, int nLength );

          构造文件保留区域并与文件绑定,同时初始化保留区域

          ~filebuf( );析构函数

          fd:文件描述符

          pr:文件保留区域的指针

          nLength:文件保留区域的大小

4)。下面是4个文件保护模式

          openprot:default share/prot mode

          sh_none:   Exclusive mode ― no sharing.

          sh_read :  Read sharing allowed.

          sh_write :  Write sharing allowed.

5)。下面是2个读取文件的模式

          binary:二进制模式

          text:文本模式

6)。filebuf* attach( filedesc fd );

绑定文件保留区域到由文件描述符指定的文件

7)。filedesc  fd()const {  return  (x_fd==-1)?EOF:x_fd;  }

取得文件描述符,若保留区域对象没有与文件绑定则返回EOF,本质就是查看标识x_fd;

8)。int  is_open() const {  return (x_fd!=-1);  }

用于检测文件保留区域是否与文件绑定

9)。filebuf* open( const char* szName, int nMode, int nProt = filebuf::openprot );

打开磁盘文件且绑定文件保留区域

10)。filebuf* close();刷新文件输出缓冲区(继承streambuf特性,保留区域由输入缓冲区和输出缓冲区两部分,但不一定同时存在),关闭打开的文件并且撤消保留区域与文件之间的绑定关系(回收系统资源)。

11)。int setmode( int nMode = filebuf::text );设置读取文件模式

到此filebuf类的成员基本到齐,剩下的是几个需函数,这里就不介绍了。

感觉上这个类的内容很少,你可以任由这种感觉继续,但必须搞清楚几个问题:

1)。保留区域需要绑定到具体的流对象才能正真起作用

2)。filedesc:a typedef equivalent to int

        相当于typedef int filedesc;3)。

3)。文件描述是什么,为什么能代表文件?

4)。既然文件描述符能代表文件,它和文件之间必然存在一一对应的关系,这不是文件句柄的特性。那么文件描述符和句柄到底是什么关系,或者它们根本就是一回事?

5)。文件描述符是在哪里被设置即x_fd是在哪里被赋值?观察上面介绍的成员函数只有两种可能性:构造函数和open函数;其它如is_open和fd都只是根据标识x_fd来进行判断并没有改便它的值。

又到了从实验种找答案的时刻,带着这些问题去实验,我相信最终会成功。

2。ifstream,ofstream,fstream有一些同名又同功能的函数:

      filebuf* rdbuf() const;返回与当前流对象相关的filebuf buffer object的指针

      filedesc fd() const;

      void attach( filedesc fd );

      int is_open() const;

      int setmode( int nMode = filebuf::text );

      void open( const char* szName, int nMode, int nProt = filebuf::openprot );

功能与filebuf类的成员函数类同,不再赘述。

二。实验篇

调试环境:VC6.0调试器(也可以结合OD,它的对代码分析模块化的功能可以大大简化代码分析时的工作)

这里听取nbw巴神老哥的建议从反汇编的角度来阐述问题。

4:    #include "stdafx.h"
5:    #include "fstream.h"
6:
7:    int main(int argc, char* argv[])
8:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   push        0FFh
00401025   push        offset __ehhandler$_main (004163e9)
0040102A   mov         eax,fs:[00000000]
00401030   push        eax
00401031   mov         dword ptr fs:[0],esp
00401038   sub         esp,84h
0040103E   push        ebx
0040103F   push        esi
00401040   push        edi
00401041   lea         edi,[ebp-90h]
00401047   mov         ecx,21h
0040104C   mov         eax,0CCCCCCCCh
00401051   rep stos    dword ptr [edi]
9:        ifstream fin;
00401053   push        1
00401055   lea         ecx,[ebp-4Ch]
00401058   call        ifstream::ifstream (0040c820) //初始化文件流对象,指针为00921D00
0040105D   mov         dword ptr [ebp-4],0
10:       fin.open("test.txt");
00401064   mov         eax,[filebuf::openprot (00428fe4)]
00401069   push        eax                                        //nProt = filebuf::openprot
0040106A   push        1                                            //nMode=1 ( in )
0040106C   push        offset string "test.txt" (0042801c) //szName="test.txt"
00401071   lea         ecx,[ebp-4Ch]
00401074   call        ifstream::open (0040ce60)
11:       fin.close();
00401079   lea         ecx,[ebp-4Ch]
0040107C   call        ifstream::close (0040cec0)
12:       return 0;
00401081   mov         dword ptr [ebp-50h],0
00401088   mov         dword ptr [ebp-4],0FFFFFFFFh
0040108F   lea         ecx,[ebp-4Ch]
00401092   call        @ILT+0(ifstream::`vbase destructor') (00401005)
00401097   mov         eax,dword ptr [ebp-50h]
13:   }
0040109A   mov         ecx,dword ptr [ebp-0Ch]
0040109D   mov         dword ptr fs:[0],ecx
004010A4   pop         edi
004010A5   pop         esi
004010A6   pop         ebx
004010A7   add         esp,90h
004010AD   cmp         ebp,esp
004010AF   call        __chkesp (00401520)
004010B4   mov         esp,ebp
004010B6   pop         ebp
004010B7   ret

**********************************************************************

//来自00401074   call        ifstream::open (0040ce60)的调用

ifstream::open:
0040CE60   push        ebp
0040CE61   mov         ebp,esp
0040CE63   push        ecx
0040CE64   mov         dword ptr [ebp-4],ecx
0040CE67   mov         ecx,dword ptr [this]
0040CE6A   call        ifstream::is_open (0040cdd0) //检测文件保留区域是否于文件绑定
0040CE6F   test        eax,eax                                    //在这个时候还没有绑定
0040CE71   jne         ifstream::open+35h (0040ce95) //不跳转,执行绑定

0040CE73   mov         eax,dword ptr [prot]
0040CE76   push        eax                    
0040CE77   mov         ecx,dword ptr [mode]
0040CE7A   or          ecx,1           
0040CE7D   push        ecx               
0040CE7E   mov         edx,dword ptr [name]
0040CE81   push        edx                                   //open的三个参数入栈
0040CE82   mov         ecx,dword ptr [this]
0040CE85   call        ifstream::rdbuf (0040cac0) //取得filebuf对象的指针

0040CE8A   mov         ecx,eax                       //此处是EAX=00921D00
0040CE8C   call        filebuf::open (0040e9c0) //打开磁盘文件并且绑定文件保留区域
0040CE91   test        eax,eax                              //返回值是filebuf对象的指针00921D00
0040CE93   jne         ifstream::open+58h (0040ceb8) //跳转

0040CE95   mov        eax,dword ptr [this]
0040CE98   mov        ecx,dword ptr [eax]
0040CE9A   mov        edx,dword ptr [ecx+4]
0040CE9D   mov        eax,dword ptr [this]
0040CEA0   mov        ecx,dword ptr [eax+edx+8]
0040CEA4   or           ecx,2
0040CEA7   push        ecx
0040CEA8   mov         edx,dword ptr [this]
0040CEAB   mov         eax,dword ptr [edx]
0040CEAD   mov         ecx,dword ptr [this]
0040CEB0   add         ecx,dword ptr [eax+4]
0040CEB3   call        ios::clear (0040cd80)

0040CEB8   mov         esp,ebp
0040CEBA   pop         ebp
0040CEBB   ret         0Ch

*************************************************************************

调试到这里,对反汇编代码以及原理篇中所讲述的文件流相关函数有了更清晰的了解,但至于在实验前所提出的问题并没有涉及,甚至连“文件描述符”也没出现。

显然需要对filebuf::open 进行深入的调试,根据原理篇的介绍open函数的功能是“打开磁盘文件且绑定文件保留区域”,同时考虑到第五个问题“文件描述符是在哪里被设置?”此处是用open函数来执行打开函数功能的,因而设置“文件描述符”的位置必定在这个函数中。

这里从逻辑上坚定了调试的信心,经历过漫长调试的朋友都清楚在进入一个函数之前必定要对它的基本功能做一个全局的分析,不论分析的结果如何,但都已经在无意识间把这个函数放到程序这个大局中,而不仅仅局限于代码,这样会事半功倍。

重新调试,来到open函数。

filebuf::open:
0040E9C0   push        ebp
0040E9C1   mov         ebp,esp
0040E9C3   sub         esp,18h
0040E9C6   mov         dword ptr [ebp-14h],ecx
0040E9C9   mov         eax,dword ptr [this]
0040E9CC   cmp         dword ptr [eax+30h],0FFh   //检测文件是否已经打开
0040E9D0   je          filebuf::open+19h (0040e9d9) //跳转

0040E9D2   xor         eax,eax
0040E9D4   jmp         filebuf::open+256h (0040ec16)

0040E9D9   mov         ecx,dword ptr [mode]  //ecx=1 ( nMode=in )
0040E9DC   and         ecx,80h   //判断nMode?=binary;
0040E9E2   neg         ecx
0040E9E4   sbb         ecx,ecx
0040E9E6   and         ecx,4000h
0040E9EC   add         ecx,4000h  //状态标志字“stdio”
0040E9F2   mov         dword ptr [dos_mode],ecx //保存状态标志字
0040E9F5   mov         edx,dword ptr [mode] //edx=1 ( nMode=in )
0040E9F8   and         edx,20h //判断nMode?=nocreate
0040E9FB   test        edx,edx
0040E9FD   jne         filebuf::open+48h (0040ea08)  //不跳转
0040E9FF   mov         eax,dword ptr [dos_mode]  //取出状态标志字“stdio"
0040EA02   or          ah,1                                      //添加状态标志“showpoint"  
0040EA05   mov         dword ptr [dos_mode],eax //再次存入状态标志“stdio||showpoint"

0040EA08   mov         ecx,dword ptr [mode]  //ecx=1 ( nMode=in )
0040EA0B   and         ecx,40h  //判断nMode?=replace;
0040EA0E   test        ecx,ecx  
0040EA10   je          filebuf::open+5Bh (0040ea1b) //跳转

0040EA12   mov         edx,dword ptr [dos_mode]
0040EA15   or          dh,4
0040EA18   mov         dword ptr [dos_mode],edx

0040EA1B   mov         eax,dword ptr [mode]
0040EA1E   and         eax,8  //判断nMode?=app;
0040EA21   test        eax,eax
0040EA23   je          filebuf::open+77h (0040ea37) //跳转

0040EA25   mov         ecx,dword ptr [mode]
0040EA28   or          ecx,2
0040EA2B   mov         dword ptr [mode],ecx
0040EA2E   mov         edx,dword ptr [dos_mode]
0040EA31   or          edx,8
0040EA34   mov         dword ptr [dos_mode],edx

0040EA37   mov         eax,dword ptr [mode]
0040EA3A   and         eax,10h //判断nMode?=trunc;
0040EA3D   test        eax,eax
0040EA3F   je          filebuf::open+93h (0040ea53) //跳转

0040EA41   mov         ecx,dword ptr [mode]
0040EA44   or          ecx,2
0040EA47   mov         dword ptr [mode],ecx
0040EA4A   mov         edx,dword ptr [dos_mode]
0040EA4D   or          dh,2
0040EA50   mov         dword ptr [dos_mode],edx

0040EA53   mov         eax,dword ptr [mode]
0040EA56   and         eax,2 //判断nMode?=out;
0040EA59   test        eax,eax
0040EA5B   je          filebuf::open+0D8h (0040ea98)//跳转

0040EA5D   mov         ecx,dword ptr [mode]
0040EA60   and         ecx,1
0040EA63   test        ecx,ecx
0040EA65   je          filebuf::open+0B2h (0040ea72)
0040EA67   mov         edx,dword ptr [dos_mode]
0040EA6A   or          edx,2
0040EA6D   mov         dword ptr [dos_mode],edx
0040EA70   jmp         filebuf::open+0BAh (0040ea7a)
0040EA72   mov         eax,dword ptr [dos_mode]
0040EA75   or          al,1
0040EA77   mov         dword ptr [dos_mode],eax
0040EA7A   mov         ecx,dword ptr [mode]
0040EA7D   and         ecx,4Dh
0040EA80   test        ecx,ecx
0040EA82   jne         filebuf::open+0D6h (0040ea96)
0040EA84   mov         edx,dword ptr [mode]
0040EA87   or          edx,10h
0040EA8A   mov         dword ptr [mode],edx
0040EA8D   mov         eax,dword ptr [dos_mode]
0040EA90   or          ah,2
0040EA93   mov         dword ptr [dos_mode],eax
0040EA96   jmp         filebuf::open+0F1h (0040eab1)

0040EA98   mov         ecx,dword ptr [mode]
0040EA9B   and         ecx,1  //判断nMode?=in
0040EA9E   test        ecx,ecx
0040EAA0   je          filebuf::open+0EAh (0040eaaa)
0040EAA2   mov         edx,dword ptr [dos_mode]
0040EAA5   mov         dword ptr [dos_mode],edx
0040EAA8   jmp         filebuf::open+0F1h (0040eab1) //跳转。到此为止对nMode的判断结束,结果为nMode=stdio||showpoint,此时dos_mode=nMode=0x4100;

0040EAAA   xor         eax,eax
0040EAAC   jmp         filebuf::open+256h (0040ec16)

0040EAB1   mov         dword ptr [smode],40h   //_sopen参数之一
0040EAB8   mov         eax,[filebuf::sh_read (00428fec)]   //sh_read=0xA00
0040EABD   or          eax,dword ptr [filebuf::sh_write (00428ff0)]  //sh_write=0x400;
0040EAC3   or          eax,dword ptr [filebuf::sh_none (00428fe8)]  //sh_none=0x0;此时eax=1
0040EAC9   mov         ecx,dword ptr [share]  //ECX=0x1A4;
0040EACC   and         ecx,eax
0040EACE   mov         dword ptr [share],ecx   //保存保护模式。此时ECX=0;为sh_none
0040EAD1   cmp         dword ptr [share],0
0040EAD5   je          filebuf::open+170h (0040eb30)  //跳转

0040EAD7   mov         edx,dword ptr [share]
0040EADA   mov         dword ptr [ebp-18h],edx
0040EADD   cmp         dword ptr [ebp-18h],0C00h
0040EAE4   jg          filebuf::open+143h (0040eb03)
0040EAE6   cmp         dword ptr [ebp-18h],0C00h
0040EAED   je          filebuf::open+160h (0040eb20)
0040EAEF   cmp         dword ptr [ebp-18h],800h
0040EAF6   je          filebuf::open+14Eh (0040eb0e)
0040EAF8   cmp         dword ptr [ebp-18h],0A00h
0040EAFF   je          filebuf::open+157h (0040eb17)
0040EB01   jmp         filebuf::open+170h (0040eb30)
0040EB03   cmp         dword ptr [ebp-18h],0E00h
0040EB0A   je          filebuf::open+169h (0040eb29)
0040EB0C   jmp         filebuf::open+170h (0040eb30)
0040EB0E   mov         dword ptr [smode],10h
0040EB15   jmp         filebuf::open+170h (0040eb30)
0040EB17   mov         dword ptr [smode],20h
0040EB1E   jmp         filebuf::open+170h (0040eb30)
0040EB20   mov         dword ptr [smode],30h
0040EB27   jmp         filebuf::open+170h (0040eb30)
0040EB29   mov         dword ptr [smode],40h

0040EB30   push        180h
0040EB35   mov         eax,dword ptr [smode]
0040EB38   push        eax   //shflag
0040EB39   mov         ecx,dword ptr [dos_mode]
0040EB3C   push        ecx   //oflag
0040EB3D   mov         edx,dword ptr [name]
0040EB40   push        edx   //Filename
0040EB41   call        _sopen (00410ae0)  //返回值3

---------------------------------------------------------------------------------------------------------------
int _sopen( const char *filename, int oflag, int shflag [, int pmode ] );
filename:Filename
oflag:Type of operations allowed
shflag:Type of sharing allowed
pmode:Permission setting

在此考虑到三点:
1。“文件描述符”是在打开文件时产生的。
2。_sopen( )的作用是用来得到文件id。
3。根据原理篇可以知道open函数有两个功能,打开文件和绑定文件保留区域。显然到这里为止完成了打开文件这个功能.

根据这三点作出推测:"文件id"就是“文件描述符”.

-------------------------------------------------------------------------------------------------------------

0040EB46   add         esp,10h
0040EB49   mov         ecx,dword ptr [this]  //取得filebuf对象指针(即此时的流对象指针)
0040EB4C   mov         dword ptr [ecx+30h],eax  //文件id存入偏移流对象指针0X30处
0040EB4F   mov         edx,dword ptr [this]
0040EB52   cmp         dword ptr [edx+30h],0FFh //判断文件id是否成功
0040EB56   jne         filebuf::open+19Fh (0040eb5f)  //此时成功,跳转

0040EB58   xor         eax,eax
0040EB5A   jmp         filebuf::open+256h (0040ec16)

0040EB5F   mov         ecx,dword ptr [this]
0040EB62   call        streambuf::lock (0040d0c0) //?
0040EB67   mov         eax,dword ptr [this]
0040EB6A   mov         dword ptr [eax+34h],1  //偏移流对象头指针0X34代表什么?
0040EB71   mov         ecx,dword ptr [this]
0040EB74   call        streambuf::unbuffered (0040d320)  //返回filebuf对象的缓冲性质
0040EB79   test        eax,eax     //返回值EAX=0,表明为缓冲的
0040EB7B   jne         filebuf::open+215h (0040ebd5)    //不跳转
0040EB7D   mov         ecx,dword ptr [this]
0040EB80   call        streambuf::ebuf (0040d290)   //返回保留区域的尾指针
0040EB85   test        eax,eax                                //返回EAX=0;表明保留区域还不存在
0040EB87   jne         filebuf::open+215h (0040ebd5)  //不跳转
0040EB89   push        0A6h
0040EB8E   push        offset string "filebuf1.cpp" (0042906c)
0040EB93   push        2
0040EB95   push        200h     //保留区域的固定长度
0040EB9A   call        operator new (004101a0) //?doallocate?

------------------------------------------------------------------------------------------------------------

这个函数让我产生不小的疑惑:当执行完这个函数后的下一个函数是setb函数是用来绑定保留区域的,但到这里为止从上面的ebuf函数来看保留区域还没有分配,所以这个函数的功能必定是分配保留区域。但对于这个函数我只见过在用VC编程时来动态分配存储空间,想不到会出现在汇编代码中;还有一点就是保留区域的分配是通过doallocate函数来实现的,但这里却......

--------------------------------------------------------------------------------------------------------------
0040EB9F   add         esp,10h
0040EBA2   mov         dword ptr [ebp-10h],eax //返回值EAX=00921AD0,为保留区域指针
0040EBA5   mov         ecx,dword ptr [ebp-10h]
0040EBA8   mov         dword ptr [sbuf],ecx  //把保留区域指针存入sbuf
0040EBAB   cmp         dword ptr [sbuf],0   //判断保留区域是否成功分配
0040EBAF   jne         filebuf::open+1FDh (0040ebbd)  //跳转

0040EBB1   push        1                             //标志保留区域为无缓冲的
0040EBB3   mov         ecx,dword ptr [this]
0040EBB6   call        streambuf::unbuffered (0040d850) //设置保留区域的缓冲性质0040EBBB   jmp         filebuf::open+215h (0040ebd5)

0040EBBD   push        1   //nDelete=1;
0040EBBF   mov         edx,dword ptr [sbuf]
0040EBC2   add         edx,200h
0040EBC8   push        edx   //peb:保留区域尾地址。此时EDX=00921CD0
0040EBC9   mov         eax,dword ptr [sbuf]
0040EBCC   push        eax  //pb:保留区域首地址。此时EAX=00921AD0
0040EBCD   mov         ecx,dword ptr [this]
0040EBD0   call        streambuf::setb (0040f170)

//void setb( char* pb, char* peb, int  nDelete = 0 );

此函数在这里的作用:设置保留区域的指针,并且把保留区域绑定到

If nDelete is not 0, the reserve area will be deleted when: (1) the base pointer is changed by another setb call, or (2) the streambuf destructor is called.

0040EBD5   mov         ecx,dword ptr [mode]  //mode=1; "in"
0040EBD8   and         ecx,4 //检测是否为“ate”模式
0040EBDB   test        ecx,ecx
0040EBDD   je          filebuf::open+24Bh (0040ec0b)  //跳转

0040EBDF   mov         edx,dword ptr [mode] //这一部分代码用来回收系统资源并设置返回值为0,标志失败
0040EBE2   push        edx
0040EBE3   push        2
0040EBE5   push        0
0040EBE7   mov         eax,dword ptr [this]
0040EBEA   mov         edx,dword ptr [eax]
0040EBEC   mov         ecx,dword ptr [this]
0040EBEF   call        dword ptr [edx+0Ch]
0040EBF2   cmp         eax,0FFh
0040EBF5   jne         filebuf::open+24Bh (0040ec0b)
0040EBF7   mov         ecx,dword ptr [this]
0040EBFA   call        filebuf::close (0040d0d0)  //回收系统资源,包括刷新输出缓冲区、关闭打开的文件、撤消文件与保留区域的绑定

0040EBFF   mov         ecx,dword ptr [this]
0040EC02   call        streambuf::unlock (0040d140)
0040EC07   xor         eax,eax //返回值为0
0040EC09   jmp         filebuf::open+256h (0040ec16)

0040EC0B   mov         ecx,dword ptr [this]
0040EC0E   call        streambuf::unlock (0040d140) //?
0040EC13   mov         eax,dword ptr [this]  //保存filebuf对象指针到EAX用于函数返回。
0040EC16   mov         esp,ebp
0040EC18   pop         ebp
0040EC19   ret         0Ch

****************************************************************************

到这里,此次的调试之旅已经结束。

下面就这次实验做一下总结:

同上次一样,这次实验选取的对象也是一个简短的程序。

#include "stdafx.h"
#include "fstream.h"

int main(int argc, char* argv[])
{
          ifstream fin;
          fin.open("test.txt");
          fin.close();
          return 0;
}

注意:下面的代码只是反汇编代码的流程表达,根本不是程序。

1。显然这次实验的一切价值都蕴涵在open函数中。ifstream::open
{
       if ( !ifstream::is_open( ) )
       {
               ECX=EAX=ifstream::rdbuf( ) ;

               EAX=filebuf::open ( name, mode, prot ) ;
       }
       else
       {
              ios::clear () ;
       }

       return ;
}

2。在《深入VC流》中已经提到关于流的任何实际操作都是通过streambuf类对象对保留区域的操作来完成。在这里由于是文件流,所以最基层的操作都是filebuf::open函数中完成。其中调用streambuf类的程序函数只不过是通过类继承接替功能,本质任是在filebuf类的作用区域内。

typedef 保留区域指针 *pFileBuffer ;
typedef filebuf类对象指针 *filebuf ;
filebuf::open
{
        if( *( filebuf + 0x30 ) != 0xFF )
                   return false;

         dos_mode = nMode = { 一系列的基本运算和逻辑运算 } ;
         share = { 一系列的基本运算和逻辑运算 } ;
         smode = 0x40 ;

         EAX=_sopen ( name, dos_mode, smode ) ;
         *( filebuf + 0x30 ) = EAX ;

         if ( *( filebuf + 0x30 ) == 0xFF ) //如果文件已经打开
                  return false;

         *( filebuf + 0x34 ) = 0x1 ;

         if ( !streambuf::unbuffered( ) && !streambuf::ebuf( ) )
         { //若filebuf对象是缓冲的且保留区域尚未分配的

                  //分配保留区域
                  pFileBuffer = EAX = operator new ( 0x200, 2, "filebuf1.cpp", 0x0A6 ) ;

                  if ( pFileBuffer == 0 ) //若保留区域分配失败
                           streambuf::unbuffered ( 1 ) ; //改变保留区域性质为无缓冲
                  else //若分配保留区域
                           streambuf::ebuf ( pFileBuffer, pFileBuffer+0x200, 1 ) ; //绑定

                  if ( mode != ate )
                           return true;
                  else
                           return false;
         }
}

3。到这里对程序的流程应该清楚了,还有一点需要总结的是调试过程中出现的几个关键数据:

*filebuf         ――0X00921D00

*pFileBuffer――0X00921AD0

*( filebuf + 0x30 ) = FILE_ID

*( filebuf + 0x34 ) = 0x1 未知(那位朋友知道的话,告知一声,感激不尽!)

4。调试过程中一些未被执行的代码并没有详细的分析,但如果理解那些经过分析的代码的话,也就很容易理解了。

5。经过这次实验对文件id和文件描述符有了一定的理解,文件描述符的标志x_fd显然是在_sopen()函数中被设置的。如果还要深入那就得分析_sopen()函数了,若能力所及我会在下一篇中给予分析。

6。有一点遗憾的是到此为止还没出现文件句柄,但也有欣慰关于句柄周围的一些概念例如流对象、保留区域、文件id、文件描述等已基本搞清楚,这是不是更让我们共同期待句柄的透视?

参考资料:MSDN;fstream.h
为了降低各位阅读代码时眼睛的疲劳度,提供超星阅览器的编辑的电子版供下载
附件:DeepIntoFileStream.rar


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
2
我有个想法想征求blowfish, kanxue 两位版主的同意:
    到现在我已经发了三篇关于VC流的帖子。但由于最初时的思路不是很成熟,所以导致三篇文章的主题分散,其实从内容的逻辑上来说三者是连贯的,《从反汇编角度剖析VC中fopen的工作原理(-)》是这个专题的其实,《深入VC流》对这个专题提供了基层的理论知识,而这篇《深入文件流》则是对前两篇的深化。因而我想更改主标题为
〈文件句柄分配的原理探测〉小标题为初步探测、深入VC流、全面深入;最后还有一篇 终结篇 待写。
     我怕擅自做主顶自己的旧贴有点不合适,两位都是我敬重之人,所以想征求两位的意见。
2005-4-22 22:25
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
3
怎么两位老大都不在?
2005-4-23 16:06
0
雪    币: 50161
活跃值: (20630)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
最初由 北极星2003 发布
怎么两位老大都不在?


呵~你自己看着办(可以编辑帖子中改变主题)。
2005-4-23 16:48
0
雪    币: 99
活跃值: (2573)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
给搂主一个建议,不知道你这么费劲的分析这些是为了提高自己的汇编水平,还是就想分析那些函数的原理。如果是后一种,根本不需要在汇编级分析,不需要猜测某个成员变量的含义,所有的都有源码。关于fstream的代码在STL的fstream文件里。_sopen在CRT的open.c里。
2005-4-23 20:36
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
6
最初由 TeLeMan 发布
给搂主一个建议,不知道你这么费劲的分析这些是为了提高自己的汇编水平,还是就想分析那些函数的原理。如果是后一种,根本不需要在汇编级分析,不需要猜测某个成员变量的含义,所有的都有源码。关于fstream的代码在STL的fstream文件里。_sopen在CRT的open.c里。


首先感谢这位朋友的建议。
但不知这位朋友是否看清楚这篇文章的标题,最终目标是句柄的分配原理,这篇文章只是实现这个结果的一个阶段,我不知道现在是否有关于“句柄分配原理”这方面的详细资料,即使我所做的一切都已经有人做了或者是CRT、STL中有源码,对我个人来说都是一种创造、每一次调试的完成都意味着调试和分析能力的提高。再说源码是高级语言,我认为通过对汇编代码的分析所得到的理解必定比高级语言深刻。
在论坛中我发现这样一种现象:论坛的这个版块为《软件这个调试论坛》,似乎很多朋友都在潜意识里认为“破解软件时的调试”那才叫调试,因为我在论坛里很少看到与软件破解无关的调试分析文章。如果调试的目的仅仅是为了破解,我个人是不妥当的。如果是这样的话,看雪学院不就成了破解组织,我想看雪老大的目标也不在于此。看雪老大提供这个共同交流的平台,也希望交流面的广度和深度的拓展,这样才更有利于大家的共同进步。
这是我的一些感想,不当之处,请见量!
2005-4-23 22:13
0
雪    币: 99
活跃值: (2573)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
最初由 北极星2003 发布

提高。再说源码是高级语言,我认为通过对汇编代码的分析所得到的理解必定比高级语言深刻。
........


其实从你的文章来看,你的研究方法也是把汇编语言变成类C语言,而且你也没有最终达到你所要研究的目的。去看看相关的源码吧,你文章里未知的东西源码里面都有解答。
2005-4-23 22:36
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
8
学习!

不过理解原理看源代码更好吧。有些东西拿汇编知道怎么做或许不知道为什么要那么做。源代码更能传达作者的思想,从这一点来说,汇编代码和高级语言无所谓哪个更深刻。

看雪老大的主页主要提供的是加密原理和软件保护知识,所以论坛也以这方面为主。软件调试有很多种类和方法,不能11介绍,这里就针对加密解密的多。你看人家John Robbins写的Debugging Applications,这里很多朋友也看的。 偶个人意见。
2005-4-23 22:41
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
9
呜呜~~~~
听了各位前辈的意见,我真不知道该不该写最后一篇了?
2005-4-24 10:31
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
那就不要写了,把CRT源代码贴出来算了...
2005-4-24 16:10
0
雪    币: 212
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
这部分ms有源代码,为什么还要烦汇编????
2005-4-24 16:23
0
雪    币: 9
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
我是C++的初学者,北极星的 第二篇 深入VC流  对我帮助很大。感谢!
2008-12-6 15:36
0
雪    币: 9
活跃值: (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
从学习的角度来看,不一定要去追究文章的最终目的。分析的过程中有值得学习的地方就有意义。
2008-12-6 15:39
0
游客
登录 | 注册 方可回帖
返回
//