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的符号文件,能看到一些有用的符号,方便识别。
- PE入口点与运行时态库的关系
- C++获取PE文件的入口点
- 查找OEP PE入口点
- 获取PE文件入口点
- PE 映像运行机理(2)--- 入口
- 找单链表环的入口点
- 求链表中环的入口点
- 链表中环的入口点
- 链表中环的入口点
- FileNet PE操作入口的几个基本点
- 点与圆的关系
- PE文件的运行
- STM32中断入口地址与中断服务函数的关系
- 点与点 点与线的关系
- 什么是运行时态
- 利用sourcetree忽略一些运行时态的文件
- 应用程序的主入口点
- Android程序的入口点
- Linux下安装Vmware tools
- java学习之路之网络知识总结
- JavaSE学习笔记_13:Java-System类
- 蛇形矩阵C++
- hibernate优化(一):抓取策略
- PE入口点与运行时态库的关系
- C语言文件操作
- POJ 1279&1474 (多边形的核)
- 纯CSS实现圆角框
- java实现B-树
- 1001
- Linux启动过程详解
- 算法:归并排序
- [算法]年终奖