深入理解Windows X64调试
随着64位操作系统的普及,都开始大力进军x64,X64下的调试机制也发生了改变,与x86相比,添加了许多自己的新特性,之前学习了Windows x64的调试机制,这里本着“拿来主义”的原则与大家分享。
本文属于译文,英文原文链接:
翻译原文地址:深入Windows X64 调试
在正式开始这篇译文之前,译者先定义下面两个关于栈帧的翻译:
frame pointer:栈帧寄存器、栈帧指针,在X86平台上,是EBP所指的位置
stack pointer:栈顶寄存器、栈顶指针,在X86平台上,是ESP所指的位置
这个教程讨论一些在 X64 CPU 上代码执行的要点,如:编译器优化、异常处理、参数传递和参数恢复,并且介绍这几个topic之间的关联。我们会涉及与上述topic相关的一些重要的调试命令,并且提供必要的背景知识去理解这些命令的输出。同时,也会重点介绍X64平台的调试与X86平台的不同,以及这些不同对调试的影响。最后,我们会活学活用,利用上面介绍的知识来展示如何将这些知识应用于X64平台的基于寄存器存储的参数恢复上,当然,这也是X64平台上调试的难点。
0x00 编译器优化
这一节主要讨论影响X64 code生成的编译器优化,首先从X64寄存器开始,然后,介绍优化细节,如:函数内联处理(function in-lining),消除尾部调用(tail call elimination), 栈帧指针优化(frame pointer optimization)和基于栈顶指针的局部变量访问(stack pointer based local variable access)。
寄存器的变化
X64平台上的所有寄存器,除了段寄存器和EFlags寄存器,都是64位的,这就意味着在x64平台上所有内存的操作都是按64位宽度进行的。同样,X64指令有能力一次性处理64位的地址和数据。增加了8个新的寄存器,如: r8~r15,与其他的使用字母命名的寄存器不同,这些寄存器都是使用数字命名。下面的调试命令输出了 X64 平台上寄存器的信息:
1: kd> r rax=fffffa60005f1b70 rbx=fffffa60017161b0 rcx=000000000000007f rdx=0000000000000008 rsi=fffffa60017161d0 rdi=0000000000000000 rip=fffff80001ab7350 rsp=fffffa60005f1a68 rbp=fffffa60005f1c30 r8=0000000080050033 r9=00000000000006f8 r10=fffff80001b1876c r11=0000000000000000 r12=000000000000007b r13=0000000000000002 r14=0000000000000006 r15=0000000000000004 iopl=0 nv up ei ng nz na pe nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00000282 nt!KeBugCheckEx: fffff800`01ab7350 48894c2408 mov qword ptr [rsp+8],rcx ss:0018:fffffa60`005f1a70=000000000000007f
相比较X86平台,一些寄存器的用法已经发生变化,这些变化可以按如下分组:
不可变寄存器是那些在函数调用过程中,值被保存起来的寄存器。X64平台拥有一个扩展的不可变寄存器集合,在这个集合中,以前x86平台下原有的不可变寄存器也包含在内,新增的寄存器是从R12到R15,这些寄存器对于函数参数的恢复很重要。
Fastcall寄存器用于传递函数参数。Fastcall是X64平台上默认的调用约定,前4个参数通过RCX, RDX, R8, R9传递。
RBP不再用作栈帧寄存器。现在RBP和RBX,RCX一样都是通用寄存器,调试前不再使用RBP来回溯调用栈。
在X86 CPU中,FS段寄存器用于指向线程环境块(TEB)和处理器控制区(Processor Control Region, KPCR),但是,在X64上,GS段寄存器在用户态是指向TEB,在内核态是指向KPCR。然而,当运行WOW64程序中,FS 寄存器仍然指向32位的TEB。
在X64平台上,trap frame的数据结构(nt!_KTRAP_FRAME)中不包含不可变寄存器的合法内容。如果X64函数会使用到这些不可变寄存器,那么,指令的序言部分会保存不可变寄存器的值。这样,调试器能够一直从栈中取到这些不可变寄存器原先的值,而不是从trap frame中去取。在X64内核模式调试状态下,`.trap`命令的输出会打印一个NOTE,用于告诉用户所有从trap frame中取出的寄存器信息可能不准确,如下所示:
1: kd> kv Child-SP RetAddr : Args to Child . . . nt!KiDoubleFaultAbort+0xb8 (TrapFrame @ fffffa60`005f1bb0) . . . 1: kd> .trap fffffa60`005f1bb0 NOTE: The trap frame does not contain all registers. Some register values may be zeroed or incorrect
函数内联处理(Function in-lining)
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/67999.html