Crush The Crash--汇编级看函数调用
来源:互联网 发布:淘宝赚钱助手是真的吗 编辑:程序博客网 时间:2024/05/22 12:51
游戏在后期polish以及上线之后,一个不可避免的部分就是要处理各种bug,包括crash。
汇编?似乎只是学校里学习了一下,在现在都倾向于使用高层语言的时代,还有用么?答案是肯定的。
有大量的crash以及bug都是只发生在retail版中,现场都是优化过的汇编代码,大部分是minidump,里面包含的信息非常有限,你拿到的就是一个优化过的汇编代码,加上少量的stack上的内存信息,这种情况下要处理掉crash,能从这些汇编代码中解析minidump并最终击杀问题是唯一的选择。
本文涉及的知识在学校的时候是n本书,也有一些工具什么的需要在实际工作中积累,这里罗列一个在实际处理问题中需要了解的最小集吧。
function & thread & stack size
函数总是运行在某个线程上的,这就牵涉到一个stack size。
stack overflow
在创建线程的时候,比如CreateThread里面都允许指定stack size,如果写程序的时候,栈溢出了,那么就是知名技术网站的来历了:StackOverFlow,这时候一般会有一个异常抛出,看dump的时候一般都会有显示的说stackoverflow。
但是实际中,也遇到过直接crash,但是没有抛出这个异常,最后看下来,当前执行的地方(ESP)和栈的base pointer(EBP)的距离已经超过stacksize,这种情况下,依旧是stack overflow。
查看thread stack size
目前比较好的工具来看是vmmap:
修改thread stack size
自己创建的Thread当然可以随心去指定stack size了,但是如果想修改其他模块创建的thread的信息的话,就需要去hookCreateThread函数,使用微软的detours库可以做到这一点。这个库不是免费的了,自己玩玩就随便下一个,商业化的时候需要注意购买哦。
修改thread stack size对于大部分游戏来说并无必要,实践中,两种情况可以考虑去修改:
内存吃紧
但是如果内存非常的吃紧,游戏里集成模块很多,导致很多线程,一般线程默认是1mb的stacksize,这时候如果大部分允许256k就好的话,那么就有可能节省几十MB,这就是很有意义的一件事。
其他模块的锅
实际中也遇到过nvidia的某个版本的驱动,会出现stack overflow,查看下来是这些thread创建了64kb stacksize的thread,这时候的hook再次出马搞定。
函数调用过程
调用一个函数(比如void foo())涉及到几个因素:传的参数,返回地址和运行过程中使用的空间。
stack frame
一个函数调用,需要的stack上的空间我们称为stack frame,这部分地址:
- 起始部分存在寄存器EBP(base pointer)
- 结束地址放在寄存器ESP(stack pointer)
看下一个函数调用汇编代码就很清晰了:
00B7D4B0 push ebp 00B7D4B1 mov ebp,esp 00B7D4B3 and esp,0FFFFFFF0h 00B7D4B6 sub esp,354h可以看到,进入一个函数调用之后,ebp被push一次,然后上一级的stack pointer作为这一级function的base pointer,一个mov操作,然后esp再做减法,这之间的空间就是stack frame了。
再看下函数返回:
00B7D4FA mov esp,ebp 00B7D4FC pop ebp 00B7D4FD ret 18hebp回赋给esp,然后pop出ebp,回复到调用函数之前的样子。
esp使用
了解了这些之后,遇到一个minidump,一看,都是优化的汇编代码,但是你需要看stack上的信息,就可以使用esp寄存器来查看:
ebp使用
ebp有一些更多的信息:
16(%ebp)- third function parameter12(%ebp)- second function parameter8(%ebp)- first function parameter4(%ebp)- old %EIP (the function's "return address")0(%ebp)- old %EBP (previous function's base pointer)-4(%ebp)- first local variable-8(%ebp)- second local variable-12(%ebp)- third local variable从这里我们可以看到,参数的传递也是通过ebp可以得到:
当然上面这个case是无意之举,visual studio可以正确的显示出参数,其他一些情况,visual studio不能用的时候,通过ebp来这样看参数就有意义了。
esi
上面主要说的是stack frame的ebp和esp,还有一个寄存器ESI也比较重要,它是用来存放当前代码执行到那里的地址(代码也是一块内存么)。
之前有一次出现crash,dump里看不出是那个模块crash了,那么通过ESI可以确定crash的内存地址,然后再查询哥哥module的地址区域,最后就可以确定出是那个module crash了。
这个也是非常有帮助的。
reference:
- Crush The Crash--汇编级看函数调用
- Crush The Crash--蓝屏
- Crush The Crash--dump和异常
- VS2010 汇编看函数调用
- 从汇编看函数调用
- 汇编看函数调用过程
- 汇编看c之一,简单函数调用
- 从汇编看c语言函数调用
- 从汇编视角看函数调用
- 从汇编看c语言函数调用
- 透过汇编另眼看世界之函数调用
- 透过汇编另眼看世界之DLL导出函数调用
- 透过汇编另眼看世界之函数调用
- 从汇编角度看英特尔x86函数调用规范
- 透过汇编另眼看世界之DLL导出函数调用
- 透过汇编另眼看世界之DLL导出函数调用
- 透过汇编另眼看世界之DLL导出函数调用
- 透过汇编另眼看世界之函数调用
- FZU 1759 Super A^B mod C(数论+快速幂+欧拉函数)
- IOS 开发笔记——如何用 collection 创建瀑布流
- ES6详解六:赋值语法糖 destructing & spread
- 关于fragment、ActionBar与ViewPager的一些总结
- NUKE推荐的cybersecurityventures
- Crush The Crash--汇编级看函数调用
- iOS--textField
- 将字符串写入 data/data/包名/files
- 软件工程之软件生存周期
- storyboard设置圆角按钮
- markdown语法规则
- HDU 3695 Computer Virus on Planet Pandora
- rsync命令
- mysql进程意外停止的解决方案