【VC】编译时Debug模式和Release模式下函数指针/调用的区别
phpskycn2012/08/12软件综合 IP:浙江
今年遇到一个很纠结的问题,代码改好后Debug模式下测试无误,
于是在又在Release模式下编译了一遍,后来无意中运行并测试了一遍,突然报错。
E1.png
检查了一遍代码没有被修改过,Debug模式下编译正常。
这时候只好看看报错时给出的信息了。
既然是内存不能写入,追踪EIP寄存器的值,执行的指令是:

mov byte ptr[ebx],al

其中ebx的值自己取一个函数指针的值,并且在此之前,修改过该对应页的保护属性,运行写入。
一开始因为发现(DEBUG模式下)函数指针并没有指向该函数,而是指向IAT(导入表)中对应的地方,
这样一来写入的内容等于是写到了导入表中,但是采取了一点小策略:
(pfun是函数指针)

mov eax,pfun
add eax,[eax+1]
add eax,5
mov pfun,eax

这段代码的作用是修正函数指针使其指向函数的位置。具体原理:
PE格式中导入表其实是一串跳转指令,具体跳转的位置由PE Loader在加载时填入,使得程序编译时不用考虑
加载时的位置情况。
其中使用的是JMP Near
指令为 E9 XX XX XX XX(函数地址)
上面的代码先获取这条指令的地址,再得到函数具体的地址(跳过头上的字节就OK),
该地址为偏移量,加上这个偏移量后再加5(为什么?这条指令的长度是5,得跳过这条指令本身),就能得到函数地址。
//===================回归正题================================
调试后发现,上诉过程得到的地址在一块空内存之中,显然出现了某些错误,
继续调试,偶然间发现一个更奇怪的现象:函数指针取到的值正式该函数的地址。
经过多次验证后,可以确认:
XXXXlease模式下编译后,函数指针得到的是该函数的地址。
XXXXBUG模式下编译后,函数指针得到的是该函数在IAT中对应的项目。
==
这个现象究竟是因为编译模式不同导致函数指针的赋值方式不同还是因为编译模式不同,函数调用模式也不同(Release下不通过IAT跳转,而是直接call函数对应的地址?),还有待探究……
+20  科创币    celeron533    2012/08/28 高质量发帖
来自:计算机科学 / 软件综合
6
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
雾雨魔理沙
12年5个月前 IP:未同步
438162
方便贴一下C++的源码看一下么
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
phpskycn作者
12年5个月前 IP:未同步
438178
回 1楼(雾雨魔理沙) 的帖子
这个东西90%是内联汇编写的[s:222]
相关部分已经贴出来了
补充:函数调用方式为naked
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
雾雨魔理沙
12年5个月前 IP:未同步
438221
这样的话…就只能从理论层面范范的说了。

首先,所谓release模式呢,即为发布版本。
脱离环境可以在操作系统独立运行,编译器生成release版本的时候会做一些优化和精简。

帧指针的省略就是其中的一种精简方式,
函数调用原理来说,函数调用过程中,会将函数信息存于栈中,函数信息通常包括参数,返回等。
如果出现函数实现和函数声明不匹配,程序必然down掉。(常见的run time error起因)

debug模式下,栈的访问是靠寄存器EBP保存的地址来访问的,这样的访问通常比较安全。
而release模式下,优化会省略掉EBP栈基址指针,通过一个全局指针来访问栈,就有可能出现程序release版本down掉的结果。

LZ的汇编码已经反应出两种编译版本,函数指针取址的区别了,所以我觉着这种可能大一些。

具体解决办法,我用的不是私人的电脑没有vc环境,建议多上网搜搜,应该是改release模式里边的一些参数
好久不用 已然记不清楚了 呵呵。。
+100
科创币
phpskycn
2012-08-13
+100
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
phpskycn作者
12年5个月前 IP:未同步
438599
回 3楼(雾雨魔理沙) 的帖子
昨天看PE File相关资料时无意看到:
DEBUG模式下编译的程序会使用重定位表,可能经过重定位处理。
release模式则不用……
怀疑跟这个有关。
个人认为虽然C++支持内联汇编,不过在结合上问题还是很多的,比如说,某个void型函数不使用任何局部变量,也没有参数,可是VC照样会平衡堆栈……
看来这样大量内联汇编还是很蛋疼的[s:273]
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
雾雨魔理沙
12年5个月前 IP:未同步
438919
这个问题,我今天偷空查阅了一些资料,也包括曾经长泡的一些坛子,
release版本出现问题的原因还当真不少——至少超出了我的想象。

总得来说,debug模式的编译步骤相对规范,安全,但是效率和空间的代价都太大……
而release模式的编译存在的风险当真不少,变量 指针 函数指针的初始化的重要性在这时候就看出来了。
而且C++最为强类型语言,void型很多时候都是隐患呐。。虽然void型确实好用又方便- -

内联汇编的问题,很多前辈提及都是说过尽量回避的,
除非对效率有着极其之高的要求。。
当初还不是很明白前辈们的一些经验之谈,随着时间的推移,也逐渐理解了一点点,
就比如lz说的编译器的结合问题,有时候就很难做到完美无缺… 虽然我们用着vc这个产品,
但这一点我们倒也无法直接吐槽ms
越是了解的深,就越是理解底层工程师的艰辛和了不起……
+50
科创币
phpskycn
2012-08-14
有道理
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
phpskycn
专家 老干部 学者 机友 笔友
文章
402
回复
4591
学术分
8
2009/03/15注册,2个月6天前活动

CV

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}