PE入口点与运行时态库的关系

来源:互联网 发布:我的电脑打不开淘宝网 编辑:程序博客网 时间:2024/06/06 02:10

使用VC环境开发的程序入口点处的代码并非是所谓的main函数的代码,而是运行时态库代码,也就是说VC在链接生成PE文件时,PE入口点处的代码是VC自动添加上去的,在这段自动添加的代码中对运行时态库进行了初始化,并将程序运行时装载的基地址传给CRT入口点函数,然后由CRT调用了用户编写的MAIN函数。

CVP中以追加方式将FileInfect追加到MessageBox后面时需要重定位FileInfect,以便其能正确加载,在重定位过程中:

1、FileInfect的重定位信息不能丢失,(在VS2015中,可使用随机基址链接选项,强制VC为该文件生成重定位数据。)

2、上述向CRT代码中传入的基地址不能被重定位,因为这个基地址是MessageBox+FileInfect的加载基址。所以在修改FileInfect的重定位表示需要跳过这个特殊的数据,否则文件追加后运行会失败。

但VS2010与VS2015在生成PE时自动添加的CRT代码不同,甚至DEBUG与release版本也不同。为了在FileInfect中定位这个基址,只能暴力搜索了。

一、如何定位入口点处的CRT代码

(1)在VS2010中,在FileInfect的InitInstance()开始处设断,然后调试运行,停下后查看函数调用栈,直接查看crt对应的入口函数,找到对Wmain()函数的调用。

//跳过__tmainCRTStartup(crtexe.h)中调用WinMain处的重定位修改,否则入口地址出错。/*debug版本下00143CF9 8B 4D 80             mov         ecx,dword ptr [ebp-80h]  00143CFC 51                   push        ecx  001C3CFD 8B 55 9C             mov         edx,dword ptr [ebp-64h]  001C3D00 52                   push        edx  001C3D01 6A 00                push        0  001C3D03 68 00 00 1C 00       push        1C0000h  001C3D08 E8 93 32 00 00       call        wWinMain (1C6FA0h)  *//*release下00D931B3 EB 03                jmp         __tmainCRTStartup+14Bh (0D931B8h)  00D931B5 6A 0A                push        0Ah  00D931B7 59                   pop         ecx  00D931B8 51                   push        ecx  00D931B9 50                   push        eax  00D931BA 53                   push        ebx  00D931BB 68 00 00 D9 00       push        0D90000h  00D931C0 E8 BB 07 00 00       call        wWinMain (0D93980h)  */

CALL指令之前的压入堆栈的数据就是所要找的基址。因为WIN7以后系统采用随机基址的机制,这个加载基址随时发生变化。所以考虑使用基址前的8字节的硬编码指令来定位,越长愈好,免得定位错误。在DEBUG下是0X68006A52和0x9C558B51,在RELEASE下是0x68535051和0x590A6A03。

(2)在VS2015下,CRT发生了较大变化,同样下断点调试,但函数栈不好用了,看不到CRT的调用,直接查看汇编代码,然后根据入口点地址,找到FileInfect的入口代码,对Wmain()函数调用的代码就在那里等你。

//DEBUG版本下
--- f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl ---------------------002D8E90 55                   push        ebp  002D8E91 8B EC                mov         ebp,esp  002D8E93 E8 38 13 00 00       call        __scrt_get_show_window_mode (02DA1D0h)  002D8E98 0F B7 C0             movzx       eax,ax  002D8E9B 50                   push        eax  002D8E9C E8 79 32 00 00       call        __get_wide_winmain_command_line (02DC11Ah)  002D8EA1 50                   push        eax  002D8EA2 6A 00                push        0  002D8EA4 68 00 00 2D 00       push        offset __ImageBase (02D0000h)  002D8EA9 E8 32 34 00 00       call        wWinMain (02DC2E0h)  002D8EAE 5D                   pop         ebp  002D8EAF C3                   ret  --- f:\dd\vctools\crt\vcstartup\src\startup\exe_wwinmain.cpp -------------------002D8EB0 55                   push        ebp  002D8EB1 8B EC                mov         ebp,esp  002D8EB3 E8 58 FD FF FF       call        __scrt_common_main (02D8C10h)  002D8EB8 5D                   pop         ebp  002D8EB9 C3                   ret  
//Release下00EF3388 E8 15 0C 00 00       call        ___telemetry_main_invoke_trigger (0EF3FA2h)  00EF338D 59                   pop         ecx  00EF338E E8 5D 07 00 00       call        __scrt_get_show_window_mode (0EF3AF0h)  00EF3393 0F B7 C0             movzx       eax,ax  00EF3396 50                   push        eax  00EF3397 E8 42 0C 00 00       call        __get_wide_winmain_command_line (0EF3FDEh)  00EF339C 50                   push        eax  00EF339D 57                   push        edi  00EF339E 68 00 00 EF 00       push        offset __ImageBase (0EF0000h)  00EF33A3 E8 B9 0C 00 00       call        wWinMain (0EF4061h)  00EF33A8 8B F0                mov         esi,eax  

同样找到8字节的硬编码指令作为定位的标志。 在DEBUG下是0x68006A50和0x00003279,在RELEASE下是0x68575000和0x000C42E8。

二、在修改FileInfect的重定位表时,跳过该基址。

基址所在的位置已经定位了,现在跳过它。

#if _MSC_VER == 1600  //"vc100  VC2010"#ifdef _DEBUGif ((*(RelocAddress - 2) == 0x9C558B51) && (*(RelocAddress - 1) == 0x68006A52))#elseif ((*(RelocAddress - 2) == 0x590A6A03) && (*(RelocAddress - 1) == 0x68535051))#endif#endif#if _MSC_VER == 1900   //"vc140  VC2015"#ifdef _DEBUGif ((*(RelocAddress - 2) == 0x00003279) && (*(RelocAddress - 1) == 0x68006A50))#elseif ((*(RelocAddress - 2) == 0x000C42E8) && (*(RelocAddress - 1) == 0x68575000))#endif#endif{TRACE(_T("跳过__tmainCRTStartup中调用WinMain处的重定位修改\n"));}else{*RelocAddress= *RelocAddress + NewImageBase - pNtHeader->OptionalHeader.ImageBase;}

三、其他问题

(1)这种硬编码的提取最好在 FileInfect已经编程完工后进行,不然可能提取的不是最终的位置,程序改动或编译选项调整都可能导致其机器码发生变化,那么硬编码可能就错误了。

(2)换了编译环境,就重做这个过程吧,CRT代码肯定发生变化。

(3)调试FileInfect入口时,最好联网自动下载CRT的符号文件,能看到一些有用的符号,方便识别。






0 0