在上篇:PE手工分析-PE头 中我们了解了PE文件头内容,在此基础上我们来分析一下导入表和导入函数地址表的内容.

还是使用上篇使用的PE文件来分析,上一篇中我们基本上已经定位出PE头的位置以及相应内容.

PE扩展头中包含 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表

通过该数据目录表我们可以进一步对导入表和导入函数地址表进行详细的分析(其数据目录表分析也雷同)

详细信息可以查看MSDN定义.

数据目录表结构定义:

typedef struct _IMAGE_DATA_DIRECTORY {

    DWORD   VirtualAddress;

    DWORD   Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 

DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]数据目录表分析如下:

索引

数据目录表

文件偏移地址

偏移

大小

说明

0

导出表

00000170h

00 00 00 00 

00 00 00 00

没有导出

1

导入表

00000178h

00 E0 01 00

64 00 00 00

 

 

...

 

 

 

 

12

导入地址表

000001d0h

A0 E2 01 00

3C 02 00 00

 

 

 

 

 

 

 

...

 

 

 

 

 

...

 

 

 

 

 

导入表RVA=01E000

IAT RVA=01E2A0

RVA的概念请参考:RVA,另外可以参考《加密解密ii》中的第二章介绍

了解了RVA之后我们就明白了在获取导入表的RVA之后需要根据节地址转换成文件相对地址FOA

所以为了获取导入表的文件相对地址需要对PE的节表进行分析

首先需要知道节表所在的位置(紧跟数据目录表结尾)

总共16个数据目录,导入地址函数表在第12(0开始)个位置

节表起始位置=1d0+8+3×8=1F0

节表结构如下:

typedef struct _IMAGE_SECTION_HEADER {

    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];// #define IMAGE_SIZEOF_SHORT_NAME 8

    union {

            DWORD   PhysicalAddress;

            DWORD   VirtualSize;

    } Misc;

    DWORD   VirtualAddress;

    DWORD   SizeOfRawData;

    DWORD   PointerToRawData;

    DWORD   PointerToRelocations;

    DWORD   PointerToLinenumbers;

    WORD    NumberOfRelocations;

    WORD    NumberOfLinenumbers;

    DWORD   Characteristics;

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

从上面镜像头分析可以之后该PE中包含有7个节

每个节包含40个字节

总共占有空间:40×7=280(0x118)->01F0-20E

可以看到每个节对应的内容如下:

段名称   

虚拟地址 

 虚拟大小  

物理地址  

物理大小 

  标志

PointerToRawData

.textbss

1000

0

10000

0

2EE0

 

.text

11000

 

897D

00008A00

60000020

00000400

.rdata

1A000

 

24B5

2600

40000040

8E00

.data

01D000

 

05F4

2000

C0000040

B400

.idata

0001E000

 

10C8

1200

C0000040

B600

.rsrc

00020000

 

0459

0600

40000040

C800

.reloc

00021000

 

06DA

0800

42000040

CE00

节表的分析完毕,然后我们需要针对导入表的RVA获取相应的FOA

RVA:01E000=>PointerToRawData:B600

指向导入表描述符

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

        DWORD   Characteristics;            //

        DWORD   OriginalFirstThunk;         //

    } DUMMYUNIONNAME;         //0001e270=>B600+270=b870->01E4DC->_foo@4

    DWORD   TimeDateStamp;                  // 0                                          

    DWORD   ForwarderChain;                 //0

    DWORD   Name;                 //01E4E6=>B600+4E6=BAE6->dllExport.dll

    DWORD   FirstThunk;                     //01E4AC=>B600+4AC=BAAC->01E4DC->_foo@4

} IMAGE_IMPORT_DESCRIPTOR;

其文件内容如下:

OriginalFirstThunk指向地址(RVA)01E4DC->(FOA)BADC

FirstThunk指向地址(RVA)01E4AC->(FOA)BAAC对应结构为

typedef struct _IMAGE_THUNK_DATA32 {

    union {

        DWORD ForwarderString;      // PBYTE

        DWORD Function;             // PDWORD

        DWORD Ordinal;

        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA32;

相应IMAGE_THUNK_DATA指向的内容为

typedef struct _IMAGE_IMPORT_BY_NAME {

    WORD    Hint;            //00

    BYTE    Name[1];     //_foo@4

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

这里对应的导入表分析基本完成了 

  • 相应的IAT分析如下:

RVA:01E2A0=>PointerToRawData:B600+(2A0)=B8A0在找到了IAT表的地址后让我们来看一下其中的内容

可以看到这里的BAAC正好处理B8A0的区域中.

所以很显然,FirstThunk属于IAT中的某一个地址区域

相应PE文件内容如下

Dll加载之前导入表结构

 

DLL加载之后导入表中内容将被操作系统填充为函数的VA

到这里基本上已经把IATINT绑定起来了,而在函数调用过程中将如何实现调用IAT的函数地址呢?

程序中每个调用 API 函数的 CALL 指令所使用的地址都是相应函数登记在 IAT 表的地址

源文档 <http://blog.csdn.net/misterliwei/article/details/840983>

 

那么,IAT导入函数地址表,和导入表有什么联系呢??

其实,所有的DLL的IMAGE_IMPORT_DESCRIPTOR结构的FirstThunk指向的是一片连续的内存空间,第一个DLL的IMAGE_IMPORT_DESCRIPTOR结构的FirstThunk的值,就是IAT表的起始地址!

 

也就是说,导入表中的首个IMAGE_IMPORT_DESCRIPTOR的FirstThunk字段,在内存中,等同于IMAGE_NT_HEADER.OptionHeader.DataDirectory[12].VirtualAddress

数据目录表第13项,索引值为12,就是IAT了。

 

在RING3的API劫持中,很多人都会选择使用IAT劫持,也就是基于这个理论的。

 

源文档 <http://tieba.baidu.com/f?kz=726947835>

对于IAT和导入表之间的关系用如下图片作为结尾

导入表中的FirstThunk指向IAT表中的某一项

 

之前的理解有问题所以后面加以修改

posted @ 2012-05-10 09:42 Yarkin 阅读(21) 评论(0) 编辑

要分析PE文件我们首先要对PE结构有一个大致的了解,大体上PE结构可以看成是一个平面空间里面包含有如下内容

相应的MSDOS头结构定义如下,Windows加载器在加载的过程中会判断dos头是否合法

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header

    WORD   e_magic;                     // Magic number

    WORD   e_cblp;                      // Bytes on last page of file

    WORD   e_cp;                        // Pages in file

    WORD   e_crlc;                      // Relocations

    WORD   e_cparhdr;                   // Size of header in paragraphs

    WORD   e_minalloc;                  // Minimum extra paragraphs needed

    WORD   e_maxalloc;                  // Maximum extra paragraphs needed

    WORD   e_ss;                        // Initial (relative) SS value

    WORD   e_sp;                        // Initial SP value

    WORD   e_csum;                      // Checksum

    WORD   e_ip;                        // Initial IP value

    WORD   e_cs;                        // Initial (relative) CS value

    WORD   e_lfarlc;                    // File address of relocation table

    WORD   e_ovno;                      // Overlay number

    WORD   e_res[4];                    // Reserved words

    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)

    WORD   e_oeminfo;                   // OEM information; e_oemid specific

    WORD   e_res2[10];                  // Reserved words

    LONG   e_lfanew;                    // File address of new exe header

  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

通过以上结构我们可以看到e_lfanew(60=0x3c个字节偏移)字段代表exe头在文件中的位置(00F8) 

所以可以断定00F8位置指向的内容为PE头结构定义如下

typedef struct _IMAGE_NT_HEADERS {

    DWORD Signature;                                                      //PE头签名PE\0\0

    IMAGE_FILE_HEADER FileHeader;                          //PE文件头

    IMAGE_OPTIONAL_HEADER32 OptionalHeader;    //PE扩展头

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

0xF9=50 45 00 00=PE\0\0->Signature签字段

000000fch=镜像头结构开发位置占20个字节

typedef struct _IMAGE_FILE_HEADER {

    WORD    Machine;                                                     //014C-IMAGE_FILE_MACHINE_I386

    WORD    NumberOfSections;                                 //PE节数量-0007个节

    DWORD   TimeDateStamp;                                        //时间戳E72B4FA9

    DWORD   PointerToSymbolTable;                         //指向符号表0000

    DWORD   NumberOfSymbols;                                    //符号表数量0000

    WORD    SizeOfOptionalHeader;                         //扩展PE头大小00E0

    WORD    Characteristics;                                    //文件属性0102-IMAGE_FILE_32BIT_MACHINE|IMAGE_FILE_EXECUTABLE_IMAGE

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 00000110h=扩展文件头起始位置占224个字节(E0)

typedef struct _IMAGE_OPTIONAL_HEADER {

    //

    // Standard fields.

    //

 

    WORD    Magic;                                                     //010B-IMAGE_NT_OPTIONAL_HDR32_MAGIC

    BYTE    MajorLinkerVersion;                         //0A-连接器主版本号

    BYTE    MinorLinkerVersion;                         //00-连接器小版本号

    DWORD   SizeOfCode;                                           //0000008A(138)-代码节大小

    DWORD   SizeOfInitializedData;                    //0000004C(76)-已初始化数据大小

    DWORD   SizeOfUninitializedData;                //00000000(0)-为初始化数据大小

    DWORD   AddressOfEntryPoint;                         //000110AA程序入口地址

    DWORD   BaseOfCode;                                             //00001000程序段基地址

    DWORD   BaseOfData;                                             //00001000数据段基地址

 

    //

    // NT additional fields.

    //

 

    DWORD   ImageBase;                                                     //镜像加载基地址00400000

    DWORD   SectionAlignment;                                     //节对其0001000(4096)

    DWORD   FileAlignment;                                            //文件对齐0000200(512)

    WORD    MajorOperatingSystemVersion;             //操作系统主版本号0005

    WORD    MinorOperatingSystemVersion;             //操作系统小版本号0001

    WORD    MajorImageVersion;                                   //镜像主版本号0000

    WORD    MinorImageVersion;                                   //镜像小版本号0000

    WORD    MajorSubsystemVersion;                           //子系统主版本号0005

    WORD    MinorSubsystemVersion;                            //子系统小版本号0001

    DWORD   Win32VersionValue;                                     //0

    DWORD   SizeOfImage;                                                   //镜像大小00022000

    DWORD   SizeOfHeaders;                                             //头大小0400

    DWORD   CheckSum;                                                     //0

    WORD    Subsystem;                                                   //03-IMAGE_SUBSYSTEM_WINDOWS_CUI

    WORD    DllCharacteristics;                               //8140IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE

    DWORD   SizeOfStackReserve;                               //栈初始化大小010000

    DWORD   SizeOfStackCommit;                                 //栈提交大小01000

    DWORD   SizeOfHeapReserve;                                  //堆初始化大小010000

    DWORD   SizeOfHeapCommit;                                    //堆提交大小01000

    DWORD   LoaderFlags;                                                //0

    DWORD   NumberOfRvaAndSizes;                               //10(16)

    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

到这里我们基本上已经将PE文件头信息给分析完成了.

下一篇我们来了解一下《导入表和函数地址导入表》

posted @ 2012-05-10 09:13 Yarkin 阅读(14) 评论(0) 编辑

RVA是相对虚拟地址(Relative Virtual Address)的缩写,顾名思义,它是一个“相对”地址,也可以说是“偏移量”,PE文件的各种数据结构中涉及到地址的字段大部分都是以RVA表示的。

准 确地说,RVA就是当PE文件被装载到内存中后,某个数据的位置相对于文件头的偏移量。举个例子,如果Windows装载器将一个PE文件装入 00400000h处的内存中,而某个节中的某个数据被装入0040xxxxh处,那么这个数据的RVA就是(0040xxxxh- 00400000h)=xxxxh,反过来说,将RVA的值加上文件被装载的基地址,就可以找到数据在内存中的实际地址。

PE文件中出现RVA的 概念是因为PE的内存映像和磁盘文件映像是不同的,同一数据相对于文件头的偏移量在内存中和在磁盘文件中可能是不同的,为了提高效率,PE文件头中使用的 都是内存映像中的偏移量,也就是RVA。从图17.3中也可以得到另一个结论,那就是RVA仅仅是对于处于节中的数据而言的,对于文件头和节表来说无所谓 RVA和文件偏移,因为它们在被映射到内存中后不管是大小还是偏移都不会有任何改变。

 

2、汇编中虚拟地址(VRA)与文件偏移地址(FileOffset)的相互转换:

+---------+---------+---------+---------+---------+---------+

|  段名称   虚拟地址  虚拟大小  物理地址  物理大小   标志   |

+---------+---------+---------+---------+---------+---------+

|  Name     VOffset    VSize    ROffset    RSize      Flags |

+---------+---------+---------+---------+---------+---------+

|  .text   00001000   00000092  00000400  00000200  60000020|

|  .rdata  00002000   000000F6  00000600  00000200  40000040|

|  .data   00003000   0000018E  00000800  00000200  C0000040|

|  .rsrc   00004000   000003A0  00000A00  00000400  C0000040|

+---------+---------+---------+---------+---------+---------+

文件虚拟偏移地址和文件物理偏移地址的计算公式如下:

 

>>>>>>>VaToFileOffset( 虚拟地址转文件偏移地址)

如VA = 00401000 (虚拟地址)

ImageBase = 00400000 (基地址)

VRk = VOffset - ROffset = 00001000 - 00000400 = C00 (得出文件虚拟地址和文件物理址之间的VRk值)

FileOffset = VA - ImageBase - VRk = 00401000 - 00400000 - C00 = 400(文件物理地址的偏移地址)

 

如VA = 00401325,则:

FileOffset = VA - ImageBase - VRk = 00401325 - 00400000 - C00 = 725

 

>>>>>>FileOffsetToVa( 文件偏移地址转虚拟地址)

如FileOffset = 435(文件偏移地址)

VA = FileOffset + ImageBase + VRk = 435 + 00400000 + C00 = 00401035(虚拟地址)

 

源文档 <http://blog.csdn.net/xuexi1028/article/details/6948591>

 

posted @ 2012-05-09 22:55 Yarkin 阅读(14) 评论(0) 编辑
char str1[]       = "abc";
char str2[]       = "abc";
const char str3[] = "abc"; 
const char str4[] = "abc"; 
const char* str5  = "abc";
const char* str6  = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?
cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?
cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?

输出结果是什么?

false
false
true

why?反汇编看看

	char str1[]       = "abc";
00CD163E  mov         eax,dword ptr [string "abc" (0CD9A40h)]  //将abc字符串(静态地址)
00CD1643  mov         dword ptr [str1],eax //str1地址 0x0018fd00 
	char str2[]       = "abc";
00CD1646  mov         eax,dword ptr [string "abc" (0CD9A40h)]  
00CD164B  mov         dword ptr [str2],eax  //str2地址 0x0018fcf4 
	const char str3[] = "abc"; 
00CD164E  mov         eax,dword ptr [string "abc" (0CD9A40h)]  
00CD1653  mov         dword ptr [str3],eax  //str3地址 0x0018fce8
	const char str4[] = "abc"; 
00CD1656  mov         eax,dword ptr [string "abc" (0CD9A40h)]  
00CD165B  mov         dword ptr [str4],eax  //str4地址 0x0018fcdc
	const char* str5  = "abc";
00CD165E  mov         dword ptr [str5],offset string "abc" (0CD9A40h)  //str5指向0CD9A40h
	const char* str6  = "abc";
00CD1665  mov         dword ptr [str6],offset string "abc" (0CD9A40h)  //str6指向0CD9A40h
	cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?
00CD166C  mov         esi,esp  
00CD166E  mov         eax,dword ptr [__imp_std::endl (0CDD31Ch)]  
00CD1673  push        eax  
00CD1674  lea         ecx,[str1]  
00CD1677  lea         edx,[str2]  
00CD167A  cmp         ecx,edx  
00CD167C  sete        al  
00CD167F  mov         edi,esp  
00CD1681  movzx       ecx,al  
00CD1684  push        ecx  
00CD1685  mov         ebx,esp  
00CD1687  push        offset std::boolalpha (0CD1113h)  
00CD168C  mov         ecx,dword ptr [__imp_std::cout (0CDD318h)]  
00CD1692  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD30Ch)]  
00CD1698  cmp         ebx,esp  
00CD169A  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
00CD169F  mov         ecx,eax  
00CD16A1  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD308h)]  
00CD16A7  cmp         edi,esp  
00CD16A9  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
00CD16AE  mov         ecx,eax  
00CD16B0  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD310h)]  
00CD16B6  cmp         esi,esp  
00CD16B8  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
	cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?
00CD16BD  mov         esi,esp  
00CD16BF  mov         eax,dword ptr [__imp_std::endl (0CDD31Ch)]  
00CD16C4  push        eax  
00CD16C5  lea         ecx,[str3]  
00CD16C8  lea         edx,[str4]  
00CD16CB  cmp         ecx,edx  
00CD16CD  sete        al  
00CD16D0  mov         edi,esp  
00CD16D2  movzx       ecx,al  
00CD16D5  push        ecx  
00CD16D6  mov         ebx,esp  
00CD16D8  push        offset std::boolalpha (0CD1113h)  
00CD16DD  mov         ecx,dword ptr [__imp_std::cout (0CDD318h)]  
00CD16E3  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD30Ch)]  
00CD16E9  cmp         ebx,esp  
00CD16EB  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
00CD16F0  mov         ecx,eax  
00CD16F2  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD308h)]  
00CD16F8  cmp         edi,esp  
00CD16FA  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
00CD16FF  mov         ecx,eax  
00CD1701  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD310h)]  
00CD1707  cmp         esi,esp  
00CD1709  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
	cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?
00CD170E  mov         esi,esp  
00CD1710  mov         eax,dword ptr [__imp_std::endl (0CDD31Ch)]  
00CD1715  push        eax  
00CD1716  mov         ecx,dword ptr [str5]  
00CD1719  cmp         ecx,dword ptr [str6]  
00CD171C  sete        dl  
00CD171F  mov         edi,esp  
00CD1721  movzx       eax,dl  
00CD1724  push        eax  
00CD1725  mov         ebx,esp  
00CD1727  push        offset std::boolalpha (0CD1113h)  
00CD172C  mov         ecx,dword ptr [__imp_std::cout (0CDD318h)]  
00CD1732  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD30Ch)]  
00CD1738  cmp         ebx,esp  
00CD173A  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
00CD173F  mov         ecx,eax  
00CD1741  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD308h)]  
00CD1747  cmp         edi,esp  
00CD1749  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  
00CD174E  mov         ecx,eax  
00CD1750  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0CDD310h)]  
00CD1756  cmp         esi,esp  
00CD1758  call        @ILT+505(__RTC_CheckEsp) (0CD11FEh)  

 

所以这里

 

str1(0x0018fd00)!=str2(0x0018fcf4)

 

str3(0x0018fce8)!=str4(0x0018fcdc)

str5(0x0CD9A40h)==str6(0x0CD9A40h)

结果为

false

false

true  

 

 

 

  

  

posted @ 2012-05-06 15:45 Yarkin 阅读(9) 评论(0) 编辑

直接上代码

float a = 1.0f;
cout << (int)a << endl;
cout << (int&)a << endl;
cout << boolalpha << ( (int)a == (int&)a ) << endl; // 输出什么?
float b = 0.0f;
cout << (int)b << endl;
cout << (int&)b << endl;
cout << boolalpha << ( (int)b == (int&)b ) << endl; // 输出什么?

请问输出结果如何?

1
1065353216
false
0
0
true

为什么0.0f和1.0f有这么大的差别呢?让我们反汇编看看代码如何?

00931620  push        ebp  
00931621  mov         ebp,esp  
00931623  sub         esp,0D8h  
00931629  push        ebx  
0093162A  push        esi  
0093162B  push        edi  
0093162C  lea         edi,[ebp-0D8h]  
00931632  mov         ecx,36h  
00931637  mov         eax,0CCCCCCCCh  
0093163C  rep stos    dword ptr es:[edi]  
	float a = 1.0f;
0093163E  fld1  //将1.0f装载到st(0)
00931640  fstp        dword ptr [a]  
	cout << (int)a << endl;
00931643  mov         esi,esp  
00931645  mov         eax,dword ptr [__imp_std::endl (93D31Ch)]  
0093164A  push        eax  
0093164B  fld         dword ptr [a]  //st0 = a
0093164E  call        @ILT+340(__ftol2_sse) (931159h)  //具体做什么不太清楚好像望城了从float到long类型的转换,得到的值为1
00931653  mov         edi,esp  
00931655  push        eax  
00931656  mov         ecx,dword ptr [__imp_std::cout (93D318h)]  
0093165C  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D314h)]  
00931662  cmp         edi,esp  
00931664  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
00931669  mov         ecx,eax  
0093166B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D310h)]  
00931671  cmp         esi,esp  
00931673  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
	cout << (int&)a << endl;
00931678  mov         esi,esp  
0093167A  mov         eax,dword ptr [__imp_std::endl (93D31Ch)]  
0093167F  push        eax  
00931680  mov         edi,esp  
00931682  mov         ecx,dword ptr [a]  //输出a地址内容(强制将内容转换为int类型),a内存中的内容为3f800000(float的编码方式),强制转换为int类型得到了3f800000的10进制值
00931685  push        ecx  
00931686  mov         ecx,dword ptr [__imp_std::cout (93D318h)]  
0093168C  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D314h)]  
00931692  cmp         edi,esp  
00931694  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
00931699  mov         ecx,eax  
0093169B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D310h)]  
009316A1  cmp         esi,esp  
009316A3  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
	cout << boolalpha << ( (int)a == (int&)a ) << endl; // 输出什么?
009316A8  mov         esi,esp  
009316AA  mov         eax,dword ptr [__imp_std::endl (93D31Ch)]  
009316AF  push        eax  
009316B0  fld         dword ptr [a] 
009316B3  call        @ILT+340(__ftol2_sse) (931159h)  
009316B8  cmp         eax,dword ptr [a]  //(1==0x3f800000)肯定输出false
009316BB  sete        cl  
009316BE  mov         edi,esp  
009316C0  movzx       edx,cl  
009316C3  push        edx  
009316C4  mov         ebx,esp  
009316C6  push        offset std::boolalpha (931113h)  
009316CB  mov         ecx,dword ptr [__imp_std::cout (93D318h)]  
009316D1  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D30Ch)]  
009316D7  cmp         ebx,esp  
009316D9  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
009316DE  mov         ecx,eax  
009316E0  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D308h)]  
009316E6  cmp         edi,esp  
009316E8  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
009316ED  mov         ecx,eax  
009316EF  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D310h)]  
009316F5  cmp         esi,esp  
009316F7  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
	float b = 0.0f;
009316FC  fldz  
009316FE  fstp        dword ptr [b]  
	cout << (int)b << endl;
00931701  mov         esi,esp  
00931703  mov         eax,dword ptr [__imp_std::endl (93D31Ch)]  
00931708  push        eax  
00931709  fld         dword ptr [b]  
0093170C  call        @ILT+340(__ftol2_sse) (931159h)  
00931711  mov         edi,esp  
00931713  push        eax  
00931714  mov         ecx,dword ptr [__imp_std::cout (93D318h)]  
0093171A  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D314h)]  
00931720  cmp         edi,esp  
00931722  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
00931727  mov         ecx,eax  
00931729  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D310h)]  
0093172F  cmp         esi,esp  
00931731  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
	cout << (int&)b << endl;
00931736  mov         esi,esp  
00931738  mov         eax,dword ptr [__imp_std::endl (93D31Ch)]  
0093173D  push        eax  
0093173E  mov         edi,esp  
00931740  mov         ecx,dword ptr [b]  
00931743  push        ecx  
00931744  mov         ecx,dword ptr [__imp_std::cout (93D318h)]  
0093174A  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D314h)]  
00931750  cmp         edi,esp  
00931752  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
00931757  mov         ecx,eax  
00931759  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D310h)]  
0093175F  cmp         esi,esp  
00931761  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
	cout << boolalpha << ( (int)b == (int&)b ) << endl; // 输出什么?
00931766  mov         esi,esp  
00931768  mov         eax,dword ptr [__imp_std::endl (93D31Ch)]  
0093176D  push        eax  
0093176E  fld         dword ptr [b]  
00931771  call        @ILT+340(__ftol2_sse) (931159h)  
00931776  cmp         eax,dword ptr [b]  //b对应内容为0x00000000,转换之后依然为0所以相等
00931779  sete        cl  
0093177C  mov         edi,esp  
0093177E  movzx       edx,cl  
00931781  push        edx  
00931782  mov         ebx,esp  
00931784  push        offset std::boolalpha (931113h)  
00931789  mov         ecx,dword ptr [__imp_std::cout (93D318h)]  
0093178F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D30Ch)]  
00931795  cmp         ebx,esp  
00931797  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
0093179C  mov         ecx,eax  
0093179E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D308h)]  
009317A4  cmp         edi,esp  
009317A6  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  
009317AB  mov         ecx,eax  
009317AD  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (93D310h)]  
009317B3  cmp         esi,esp  
009317B5  call        @ILT+505(__RTC_CheckEsp) (9311FEh)  

如此就可以推导出结果对应为

1

0x3f800000

false

0

0  

true

posted @ 2012-05-06 15:26 Yarkin 阅读(7) 评论(0) 编辑

近期公司希望能够开始对项目流程进行整顿,所以希望我能够对团队成员提供一些相关培训.

今天是第一天,也是第一次对团队成员进行培训.这里准备了一点ppt作为自己培训的资料.

 

作为第一天开始,个人思路是总结历史,展望未来.如果历史可以给我们再来一次的机会我们会如何做?

 

1,回顾历史PPT下载

 

在会上发出纸请与会同事针对以往的项目经验提出自己的一些相关意见.

 

收集的意见还真不少,主要集中:

需求不明确
沟通不顺畅
时间紧张
版本发布混乱
没有文档
代码不规范
随意更改需求
没有统一的缺陷管理平台

这几点在PPT中我也有提到,那么可能大家都是深有感触,针对这些问题我们分类过了一遍

问题1:需求不明确,随意更改需求

 针对这个问题大家一致认为,在接受到需求之后需要形成一个规范的处理流程

1:需求人员-开发-测试一起对需求进行分析整理(产生结果未定:需求规格说明书?反正就是形成统一意识)
2:开发,测试分别针对整理的结果进行开发设计,测试用例设计(形成开发文档和测试用例)
3:在设计完成之后,增加一个复查环节,确保大家对于需求的理解和设计没有偏差,如果有偏差或者需要修改需求则重复1,2
4:大家一致确认后开始编码实现
5:编码完成添加代码审查环节
6:提交版本交予测试开发
7:发现缺陷,提交缺陷报告,开发进行分析修改.
8:形成测试报告
9:正式发布

 

但是在这个过程中可能存在缺陷等问题,如何报告缺陷/问题?沟通确实是一大障碍.

问题2:如何沟通

 

软件开发中人是关键,所以人和人之间自然需要不断的沟通交流,产生统一意识.如何有效沟通也是大家比较关注的问题.

目前初步形成意识,需要有一个统一的管理平台来支撑起从软件开发的整个生命周期.初步这里定位为TFS.

 

需求:提交到TFS中
开发:根据需求进行分析设计实现
测试:根据需求进行测试用例设计
将需求-设计实现-测试用例关联起来,形成一个统一的整体.

只有如此才可以针对部分需求(变更)进行有效的跟踪和分析,同时也作为彼此之间的交互桥梁.

 

相应的针对缺陷平台也可以将缺陷提交到TFS中,然后分配给相关开发人员进行后续的修复

其他的问题在本次讨论中没有得出过多的结果,只是在PPT的后几页中本人对相关问题进行一个小结.

当然本次讨论主要目的:

1,回顾历史,总结经验

2,抛砖引玉,为后续的培训和会议做准备

当然,目前来说还是问题阶段,但是看到这些问题又该如何解决才是最大的问题!

希望大家看到后能够帮忙提供相关意见.

谢谢!

posted @ 2012-05-04 21:33 Yarkin 阅读(6) 评论(0) 编辑

也是最近被问的一个问题,全局变量在哪个阶段初始化?

 

这个问题到没被问倒,全局变量在mainCRTStartup之后main调用之前,在该阶段应用会完成堆内存的申请(记得哪里还看到如果改了EntryPoint需要自己进行堆内存的申请和管理).

而全局变量也正是在该阶段完成的初始化.

 

然后又被问,那么全局变量在哪里被释放?回答是在应用退出之后main函数退出之后,这个回答也没问题.基本上算是正确的.

但是回头自己仔细想想,那么全局变量又是怎么样被初始化的呢?还真的有点不太清楚,所以出于好奇,今晚开始细细研究研究!

首先写了一段代码如下:

//头文件
class ClassSizeRes
{
public:
	ClassSizeRes(void);
	~ClassSizeRes(void);
};

//cpp文件
ClassSizeRes::ClassSizeRes(void)
{
}


ClassSizeRes::~ClassSizeRes(void)
{
}
//main函数处理
ClassSizeRes staticObj;
int _tmain(int argc, _TCHAR* argv[])
{
  //...
}

代码大致如此,然后在构造函数处下断点来调试,发现中断之后的调用堆栈如下:

很显然全局变量的初始化确实是在mainCRTStartup之后main调用之前,与之前所理解的确实没有差别,但是编译器又是如何处理的呢?

根据调用堆栈我们可以发现在函数_initterm的定义如下:

#ifdef CRTDLL
void __cdecl _initterm (
#else  /* CRTDLL */
static void __cdecl _initterm (
#endif  /* CRTDLL */
        _PVFV * pfbegin,
        _PVFV * pfend
        )
{
        /*
         * walk the table of function pointers from the bottom up, until
         * the end is encountered.  Do not skip the first entry.  The initial
         * value of pfbegin points to the first valid entry.  Do not try to
         * execute what pfend points to.  Only entries before pfend are valid.
         */
        while ( pfbegin < pfend )
        {
            /*
             * if current table entry is non-NULL, call thru it.
             */
            if ( *pfbegin != NULL )
                (**pfbegin)();//这里是关键,该函数就是遍历调用无参的函数指针数组
            ++pfbegin;
        }
}

接下来这里的函数指向的地址内容是关键:

这里Pfbegin=0x00f5b30c

查看0x00f5b30c对应内存的内容

0x00F5B30C  00f57f60 00f57fc0 00f58020 (后面内容为00000000)

很显然这里是一个包含3个元素的数组,那么关键就在于这3个元素指向的是什么内容

00f57f60 地址的反汇编内容如下:

ClassSizeRes staticObj;

@0

00F57F60  push        ebp 

00F57F61  mov         ebp,esp 

00F57F63  sub         esp,0C0h 

00F57F69  push        ebx 

00F57F6A  push        esi 

00F57F6B  push        edi 

00F57F6C  lea         edi,[ebp-0C0h] 

00F57F72  mov         ecx,30h 

00F57F77  mov         eax,0CCCCCCCCh 

00F57F7C  rep stos    dword ptr es:[edi] 

00F57F7E  mov         ecx,offset staticObj (0F5E1E4h) 

00F57F83  call        ClassSizeRes::ClassSizeRes (0F51190h)  //调用构造函数@1

00F57F88  push        offset `dynamic atexit destructor for 'staticObj'' (0F590A0h) 

00F57F8D  call        @ILT+190(_atexit) (0F510C3h) 

00F57F92  add         esp,4 

00F57F95  pop         edi 

00F57F96  pop         esi 

00F57F97  pop         ebx 

00F57F98  add         esp,0C0h 

00F57F9E  cmp         ebp,esp 

00F57FA0  call        @ILT+625(__RTC_CheckEsp) (0F51276h) 

00F57FA5  mov         esp,ebp 

00F57FA7  pop         ebp 

00F57FA8  ret 

 

ClassSizeRes::ClassSizeRes:

@2

00F51190  jmp         ClassSizeRes::ClassSizeRes (0F516E0h) @3

 

ClassSizeRes的构造函数

ClassSizeRes::ClassSizeRes(void)

{

@3

00F516E0  push        ebp 

00F516E1  mov         ebp,esp 

00F516E3  sub         esp,0CCh 

00F516E9  push        ebx 

00F516EA  push        esi 

00F516EB  push        edi 

00F516EC  push        ecx 

00F516ED  lea         edi,[ebp-0CCh] 

00F516F3  mov         ecx,33h 

00F516F8  mov         eax,0CCCCCCCCh 

00F516FD  rep stos    dword ptr es:[edi] 

00F516FF  pop         ecx 

00F51700  mov         dword ptr [ebp-8],ecx 

}

00F51703  mov         eax,dword ptr [this] 

00F51706  pop         edi 

00F51707  pop         esi 

00F51708  pop         ebx 

00F51709  mov         esp,ebp 

00F5170B  pop         ebp 

00F5170C  ret 

 

如此看来全局变量的初始化过程如下

Step1:编译器编译之后会根据全局变量声明来生成一些无参函数如上的@0

Step2:程序运行之后,__tmainCRTStartup会调用_initterm函数来调用编译器生成的无参函数

(**pfbegin)();//函数指针,指向编译器自动生成无参函数地址@

这里的pfbegin-pfend都是指向的编译器生成的全局变量初始化函数pfbegin

Step3:无参全局变量初始化函数pfbegin会调用各个类的构造函数完成对象初始化@1

Step3:@1会调用各类的构造函数存根地址(IAT存根地址)

Step4:@2 跳转到构造函数实际实现地址完成对象的初始化

如此到了这一步基本上已经完成了一个全局变量的初始化.

那么相应的释放又是如何实现呢?在析构函数中下断点!发现调用堆栈如下:

很显然实在doexit中调用了相应的析构函数来完成全局变量的析构

static void __cdecl doexit (
        int code,
        int quick,
        int retcaller
        )
{
#ifdef _DEBUG
        static int fExit = 0;
#endif  /* _DEBUG */

#ifdef CRTDLL
        if (!retcaller && check_managed_app())
        {
            /*
               Only if the EXE is managed then we call CorExitProcess.
               Native cleanup is done in .cctor of the EXE
               If the Exe is Native then native clean up should be done
               before calling (Cor)ExitProcess.
            */
            __crtCorExitProcess(code);
        }
#endif  /* CRTDLL */

        _lockexit();        /* assure only 1 thread in exit path */
        __TRY

        if (_C_Exit_Done != TRUE) {
            _C_Termination_Done = TRUE;

            /* save callable exit flag (for use by terminators) */
            _exitflag = (char) retcaller;  /* 0 = term, !0 = callable exit */

            if (!quick) {

                /*
                 * do _onexit/atexit() terminators
                 * (if there are any)
                 *
                 * These terminators MUST be executed in reverse order (LIFO)!
                 *
                 * NOTE:
                 *  This code assumes that __onexitbegin points
                 *  to the first valid onexit() entry and that
                 *  __onexitend points past the last valid entry.
                 *  If __onexitbegin == __onexitend, the table
                 *  is empty and there are no routines to call.
                 */

                _PVFV * onexitbegin = (_PVFV *) DecodePointer(__onexitbegin);
                if (onexitbegin) {
                    _PVFV * onexitend = (_PVFV *) DecodePointer(__onexitend);
                    _PVFV function_to_call = NULL;

                    /* save the start and end for later comparison */
                    _PVFV * onexitbegin_saved = onexitbegin;
                    _PVFV * onexitend_saved = onexitend;

                    while (1)
                    {
                        _PVFV * onexitbegin_new = NULL;
                        _PVFV * onexitend_new = NULL;

                        /* find the last valid function pointer to call. */
                        while (--onexitend >= onexitbegin && *onexitend == _encoded_null())
                        {
                            /* keep going backwards. */
                        }

                        if (onexitend < onexitbegin)
                        {
                            /* there are no more valid entries in the list, we are done. */
                            break;
                        }

                        /* cache the function to call. */
                        function_to_call = (_PVFV) DecodePointer(*onexitend);;//Decode之后指向编译器生成的资源释放处理代码

                        /* mark the function pointer as visited. */
                        *onexitend = (_PVFV)_encoded_null();

                        /* call the function, which can eventually change __onexitbegin and __onexitend */
                        (*function_to_call)();//又是一个无参函数值得关注,调用编译器生成资源释放代码,然后调用析构函数完成析构,与构造类似

                        onexitbegin_new = (_PVFV *) DecodePointer(__onexitbegin);
                        onexitend_new = (_PVFV *) DecodePointer(__onexitend);

                        if ( ( onexitbegin_saved != onexitbegin_new ) || ( onexitend_saved != onexitend_new ) )
                        {
                            /* reset only if either start or end has changed */
                            onexitbegin = onexitbegin_saved = onexitbegin_new;
                            onexitend = onexitend_saved = onexitend_new;
                        }
                    }
                }
#ifndef CRTDLL
                /*
                 * do pre-terminators
                 */
                _initterm(__xp_a, __xp_z);
#endif  /* CRTDLL */
            }

#ifndef CRTDLL
            /*
             * do terminators
             */
            _initterm(__xt_a, __xt_z);
#endif  /* CRTDLL */

#ifdef _DEBUG
            /* Dump all memory leaks */
            if (!fExit && _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_LEAK_CHECK_DF)
            {
                fExit = 1;
#ifndef CRTDLL
                __freeCrtMemory();
                _CrtDumpMemoryLeaks();
#endif  /* CRTDLL */
            }
#endif  /* _DEBUG */

        }
        /* return to OS or to caller */

        __FINALLY
            if (retcaller)
                _unlockexit();      /* unlock the exit code path */
        __END_TRY_FINALLY

        if (retcaller)
            return;


        _C_Exit_Done = TRUE;

        _unlockexit();      /* unlock the exit code path */

        __crtExitProcess(code);
}

详细的内容就不多做重复,与构造类似,从

(*function_to_call)()->编译器生成资源失败处理代码->调用到析构函数存根函数->跳转到实际的析构函数地址执行资源释放

如此基本上已经完成了全局变量资源的申请释放.

 

  

posted @ 2012-05-03 23:11 Yarkin 阅读(105) 评论(0) 编辑

许久没有回头研究c++的一些概念了,突然被一道题考住了大体上如下

class a

{}

//...

a aobj;

size_t aobjSize = sizeof(aobj);

//问aobjSize等于多少?

个人以为aobjSize=4;

毕竟需要有地址空间来储存,而已一般来说每一个对象都有一个this指针所以虽然没有任何成员变量但是仍然应该为4

事实并非如此,写了一下代码发现为1

而且反汇编之后代码如下

...

00031758 mov dword ptr [ebp-4],0
size_t dwSize = sizeof(clsObj);
0003175F mov dword ptr [ebp-24h],1//sizeof(aobj)为什么直接就是1呢?

...

沿着这个问题网上google了一下发现如下:

一、简单对象的存储

1、   基本类型对齐原则:

Char            1

Short          2

Int              4

Long            4

Float            4

Double        8

 

2、   结构体类型对齐原则:(参见《结构体对齐》一文)

以最大成员类型的对齐方式为准,即当需要增长时,增长最大成员类型所占用的字节数

 

3、   静态成员变量不占用类对象的存储空间原则static

静态成员变量所有的类对象共享一份,在静态区域中,并不占用类对象的空间。

 

4、 没有任何成员变量的类对象占用一个字节的空间

对于没有任何成员变量的类(空类),其实它并不是空的,它隐含着被编译器添加了一个char。因为实例化的原因(空类同样可以被实例化),每个实例在内存中都必须有一个独一无二的地址。因此,编译器会给一个空类隐含的加一个字节,使空类在实例化后在内存得到独一无二的地址。

 

原来如此,编译器后台判断出来为空对象之后直接赋予一个char大小的空间,如此sizeof(EmptyClassObject)自然为1

汗那个汗啊。。。

参考网址如下:

http://blog.163.com/niwei_258/blog/static/1062848820109284914229/

posted @ 2012-05-03 20:26 Yarkin 阅读(2) 评论(0) 编辑
摘要: gSoap: How to add info to SOAP Header using gSOAPThere's some misleading info in gSOAP's official documents in SOAP Header Processing Part.This article leads you to the right way and can make your program work.The use case is:Client needs to pass user name and password to Server Side to get 阅读全文
posted @ 2012-04-25 15:46 Yarkin 阅读(10) 评论(0) 编辑
摘要: TFS解决TFS不能共享文档的问题1)没有分配权限需要在项目集合门户网站中添加相应的权限,然后再到子站点中继承权限(分配权限)2)url映射问题在url映射中配置为localhost导致目标url解析不正确在SharePoint中 配置路径为IP地址阅读全文
posted @ 2012-04-25 11:03 Yarkin 阅读(9) 评论(0) 编辑