儿童EBP vs儿童SP [英] Child-EBP vs Child-SP

查看:143
本文介绍了儿童EBP vs儿童SP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在继续一些windbg教程时,我注意到一些使用 k 命令的调用栈都采用这种格式,特别是我的

While following up on some windbg tutorials I have noticed that some callstacks using k command are in this format, specially mine

Child-SP          RetAddr           Call Site

其他在线资源,例如 CodeProject k 命令以这种格式吐出信息

While other online resources like CodeProject have the k command spit out info in this format

Child-EBP          RetAddr           Call Site

我很困惑为什么我的输出与其输出之间存在差异,以及真正含义是什么.

I am confused to why there is a difference between my output and theirs and to what that truly means.

推荐答案

这取决于所使用的调用约定.某些函数分配了局部变量和参数,这些参数将传递给使用基本指针的函数,例如cdecl,但是Windows x64调用约定使用 rsp .

It depends on what calling convention is being used. Some functions allocate locals and parameters that it will pass to the functions it calls using the base pointer, such as cdecl, but Windows x64 calling convention uses rsp.

Child-SP是该帧的堆栈指针的值,它将是除帧0外的所有帧的返回地址之前的字节,帧0可能在序言之前或之中具有断点.如果断点在第一条指令上,那么 rsp 在前一帧的Child-SP上只减小了8;此 rsp 值是从陷阱帧读取的.如果您不认为被调用方的返回地址是该帧的一部分,则Child-SP是该帧编号处的帧地址(在这种情况下,这是有意义的,因为该帧的返回地址列显示了返回值地址到上一帧,成为该帧的一部分.

Child-SP is the value the stack pointer of that frame, which will be the byte before the return address for all frames except for frame 0, which might have a breakpoint before or during the prologue. If the breakpoint is on the first instruction, then the rsp would have only decreased by 8 on the Child-SP of the previous frame; this rsp value is read from the trap frame. The Child-SP is the address of the frame at the frame number if you do not consider the return address of the callee to be part of the frame (which makes sense in this scenario because the return address column of the frame is showing the return address to the previous frame to be part of this frame).

ChildEBP是 ebp 的值(不是推送到该帧的 ebp ,而是 ebp )

ChildEBP is the value of ebp when in that frame (not the ebp that is pushed to the frame, but the new value of ebp)

RetAddr是属于框架的返回地址,因此它是将返回到的地址

RetAddr is the return address that belongs to the frame, so it is the address it will return to

呼叫站点是被调用的呼叫指令之后终止帧的指令的地址(因此,基本上是被呼叫者的返回地址-呼叫指令+呼叫指令长度),或地址导致中断进入调试器的断点或其他异常(注意:异常的地址,而不是其后的指令,因此在陷阱帧中为 rip )框架0(位于堆栈顶部的框架).只要该函数不包含标签,这将指示拥有该行框架的函数的名称,并且child的args是传递给它的参数.确实,呼叫站点是它所调用的帧(位于其上方的帧)的返回地址.

The call site is the address of the instruction after the call instruction that was called that ended the frame (so basically the callee return address -- call instruction + call instruction length), or the address of the breakpoint or other exception that caused it to break into the debugger (note: the address of the exception, not the instruction after it, so this will be rip in the trap frame) in the case of frame 0 (the frame at the top of the stack). This will indicate the name of the function that owns the frame on this row as long as the function doesn't contain a label, and args to child are the arguments passed to it. Indeed the callsite is the return address of the frame that it calls (the frame above it).

传递给child的参数是传递给拥有当前行堆栈框架的函数的参数,而不是传递给由序言在分配的空间中调用的被调用函数的参数.这在x64上几乎永远都不准确,因为它显示了Homespace的前3个四元组,因为前4个参数可以在寄存器中传递,如果被调用方函数 -O0 还是不使用varargs函数,或者可以将其他内容完全放在主空间中.从儿童到儿童的Args"和"Child-SP"中的儿童"是一个错误且具有误导性的名称,表示框架调用了该函数,但实际上是指当前函数.

Args to child are the arguments passed to the function that owns the stack frame on the current row, not the arguments passed to the callee function it calls in the allocated space by the prologue. This is almost never accurate on x64, where it shows the first 3 quadwords of the homespace, because the first 4 arguments can be passed in registers, and the callee function may not home these arguments (save them in the homespace) if it is -O0 or not a varargs function, or may put something else in the homespace entirely. 'Child' in 'Args to child' and 'Child-SP' is a false and misleading name which implies the function the frame calls, but it actually refers to the current function.

网站 notepad!ShowOpenSaveDialog 上显示一个断点,这将是第一条指令.

There is an example stacktrace on this site which shows a breakpoint on notepad!ShowOpenSaveDialog, which will be the first instruction.

0:011> bp notepad!ShowOpenSaveDialog
0:011> g
Breakpoint 0 hit
notepad!ShowOpenSaveDialog:
00007ff7`0307182c 48895c2408      mov     qword ptr [rsp+8],
           rbx ss:00000073`74d2f310=0000000000000000
0:000> k
 # Child-SP          RetAddr           Call Site
00 00000073`74d2f308 00007ff7`03071aeb notepad!ShowOpenSaveDialog
01 00000073`74d2f310 00007ff7`030721fa notepad!InvokeOpenDialog+0x14f
02 00000073`74d2f370 00007ff7`030738d6 notepad!NPCommand+0x4a2
03 00000073`74d2f6f0 00007fff`664b6d41 notepad!NPWndProc+0x726
04 00000073`74d2f9f0 00007fff`664b6713 USER32!UserCallWinProcCheckWow+0x2c1
05 00000073`74d2fb80 00007ff7`03073bdb USER32!DispatchMessageWorker+0x1c3
06 00000073`74d2fc10 00007ff7`03089333 notepad!WinMain+0x27f
07 00000073`74d2fd10 00007fff`68ea3034 notepad!__mainCRTStartup+0x19f
08 00000073`74d2fdd0 00007fff`69073691 KERNEL32!BaseThreadInitThunk+0x14
09 00000073`74d2fe00 00000000`00000000 ntdll!RtlUserThreadStart+0x21

0:000> r @rcx=0
0:000> r
rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000
rdx=00000213f5020968 rsi=000000499cb1f338 rdi=00000213f5021c20
rip=00007ff70307182c rsp=000000499cb1f288 rbp=000000499cb1f2d0
 r8=00000213f4ffe9d6  r9=000000499cb1f338 r10=00000ffee060e246
r11=0000000000014140 r12=0000000000000000 r13=0000000000000001
r14=000000000004094e r15=00000000ffffffff
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
notepad!ShowOpenSaveDialog:
00007ff7`0307182c 48895c2408      mov     qword ptr [rsp+8],
rbx ss:00000049`9cb1f290=0000000000000000

0:000> ub notepad!InvokeOpenDialog+0x14f
notepad!InvokeOpenDialog+0x133:
00007ff7`03071acf 8bd8            mov     ebx,eax
00007ff7`03071ad1 85c0            test    eax,eax
00007ff7`03071ad3 782c            js      notepad!InvokeOpenDialog+0x165 (00007ff7`03071b01)
00007ff7`03071ad5 4c8b05fc080200  mov     r8,qword ptr [notepad!szOpenCaption (00007ff7`030923d8)]
00007ff7`03071adc 4c8bce          mov     r9,rsi
00007ff7`03071adf 488b5538        mov     rdx,qword ptr [rbp+38h]
00007ff7`03071ae3 498bce          mov     rcx,r14
00007ff7`03071ae6 e841fdffff      call    notepad!ShowOpenSaveDialog (00007ff7`0307182c)

nt!KiBreakpointTrap 创建的陷阱帧不是堆栈跟踪的一部分,因为陷阱帧被推送到线程的内核堆栈而不是用户堆栈.

The trap frame created by nt!KiBreakpointTrap is not part of the stack trace, because trap frames are pushed to the threads's kernel stack not the user stack.

如果您正在调试内核,那么如果存在陷阱帧并且可以访问进程地址空间,则会看到用户堆栈和内核堆栈的融合:

If you are kernel debugging then you will see a fusion of the user and kernel stacks if there is a trap frame and the process address space is accessible:

lkd> .process /P fffffa80723296f0
lkd> .reload
lkd> ld *
lkd> !process 3490                                                                                                                             
Searching for Process with Cid == 3490                                                                                                         
Cid handle table at fffff8a00195f000 with 4126 entries in use                                                                                  
                                                                                                                                               
PROCESS fffffa80723296f0                                                                                                                       
    SessionId: 1  Cid: 3490    Peb: 7fffffdf000  ParentCid: 1470                                                                               
    DirBase: 403874000  ObjectTable: fffff8a02b3a2ce0  HandleCount: 293.                                                                       
    Image: chrome.exe                                                                                                                          
    VadRoot fffffa805fc816e0 Vads 295 Clone 0 Private 16586. Modified 1536. Locked 0.                                                          
    DeviceMap fffff8a00208d0b0                                                                                                                 
    Token                             fffff8a03466d9e0                                                                                         
    ElapsedTime                       00:23:43.376                                                                                             
    UserTime                          00:00:00.717                                                                                             
    KernelTime                        00:00:00.000                                                                                             
    QuotaPoolUsage[PagedPool]         0                                                                                                        
    QuotaPoolUsage[NonPagedPool]      0                                                                                                        
    Working Set Sizes (now,min,max)  (27282, 50, 345) (109128KB, 200KB, 1380KB)                                                                
    PeakWorkingSetSize                33917                                                                                                    
    VirtualSize                       870 Mb                                                                                                   
    PeakVirtualSize                   891 Mb                                                                                                   
    PageFaultCount                    81452                                                                                                    
    MemoryPriority                    BACKGROUND                                                                                               
    BasePriority                      4                                                                                                        
    CommitCharge                      19316                                                                                                    
    Job                               fffffa805f9f46d0                                                                                         

        THREAD fffffa802e890b50  Cid 3490.469c  Teb: 000007fffffdd000 Win32Thread: fffff900c53a48c0 WAIT: (UserRequest) UserMode Non-Alertable 
            fffffa8062daf060  SynchronizationEvent                                                                                             
        Not impersonating                                                                                                                      
        DeviceMap                 fffff8a00208d0b0                                                                                             
        Owning Process            fffffa80723296f0       Image:         chrome.exe                                                             
        Attached Process          N/A            Image:         N/A                                                                            
        Wait Start TickCount      45844297       Ticks: 42 (0:00:00:00.655)                                                                    
        Context Switch Count      21234                 LargeStack                                                                             
        UserTime                  00:00:07.300                                                                                                 
        KernelTime                00:00:00.234                                                                                                 
        Win32 Start Address chrome!IsSandboxedProcess (0x000000013fbcbe90)                                                                     
        Stack Init fffff8802d5bec70 Current fffff8802d5be7c0                                                                                   
        Base fffff8802d5bf000 Limit fffff8802d5b7000 Call 0                                                                                    
        Priority 4 BasePriority 4 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5 

        Child-SP          RetAddr           Call Site                                                                                          
        fffff880`2d5be800 fffff800`0367ec32 nt!KiSwapContext+0x7a                                                                              
        fffff880`2d5be940 fffff800`0368145f nt!KiCommitThreadWait+0x1d2                                                                        
        fffff880`2d5be9d0 fffff800`0397602e nt!KeWaitForSingleObject+0x19f                                                                     
        fffff880`2d5bea70 fffff800`03678c13 nt!NtWaitForSingleObject+0xde                                                                      
        fffff880`2d5beae0 00000000`7782bd7a nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`2d5beae0)                                     
        00000000`002eec08 000007fe`fd6110ac ntdll!NtWaitForSingleObject+0xa                                                                    
        00000000`002eec10 000007fe`d97c2107 KERNELBASE!WaitForSingleObjectEx+0x79                                                              
        00000000`002eecb0 000007fe`d858aeab chrome_child!GetHandleVerifier+0x15d8417                                                           
        00000000`002eed40 000007fe`d823004d chrome_child!GetHandleVerifier+0x3a11bb                                                            
        00000000`002eedb0 000007fe`d822fc91 chrome_child!GetHandleVerifier+0x4635d                                                             
        00000000`002eee10 000007fe`d8217c59 chrome_child!GetHandleVerifier+0x45fa1                                                             
        00000000`002eee40 000007fe`d82176de chrome_child!GetHandleVerifier+0x2df69                                                             
        00000000`002ef040 000007fe`d82105d1 chrome_child!GetHandleVerifier+0x2d9ee                                                             
        00000000`002ef1b0 000007fe`d81e518b chrome_child!GetHandleVerifier+0x268e1                                                             
        00000000`002ef250 000007fe`d81e4c58 chrome_child!ChromeMain+0x377d                                                                     
        00000000`002ef570 000007fe`d81e1b2e chrome_child!ChromeMain+0x324a                                                                     
        00000000`002ef600 00000001`3faf354c chrome_child!ChromeMain+0x120                                                                      
        00000000`002ef6d0 00000001`3faf1699 chrome+0x354c                                                                                      
        00000000`002ef7c0 00000001`3fbcbe33 chrome+0x1699                                                                                      
        00000000`002efba0 00000000`775d59cd chrome!IsSandboxedProcess+0x61483                                                                  
        00000000`002efbe0 00000000`7780a561 kernel32!BaseThreadInitThunk+0xd                                                                   
        00000000`002efc10 00000000`00000000 ntdll!RtlUserThreadStart+0x1d                                                                      

陷阱帧是由 nt!KiSystemCall64 制作的,该标签中有一个名为 nt!KiSystemServiceCopyEnd 的标签,在这种情况下,它将调用系统服务 nt!NtWaitForSingleObject .陷阱帧是因此在内核堆栈上起作用的第一个堆栈帧,并从函数 nt!KiSystemCall64 rsp (Child-SP)开始,并以它分配给下一个功能. nt!KiSystemCall64 的第一条指令是 swapgs ,用于将 gs IA32_KERNEL_GS_BASE 中的 gs 交换.code>,然后将 rsp 存储到 gs:10h 中,这是KPCR中的 UserRsp 字段,然后存储 gs:1A8h 放入 rsp ,这是KPRCB中的 RspBase 字段,这显然是将用户 rsp 与内核 rsp 的线程,该线程被缓存在当前正在运行该线程的KPRCB的超线程中.然后,它开始构建陷阱帧,其中 rsp 当前根据定义(在帧结束后的字节)从陷阱帧中指向偏移量 0x190 .推送 SS ,因为当您使用 syscall 而不是 int 0x2e 时,CPU不会执行此操作.

The trap frame is made by nt!KiSystemCall64, which has a label in it called nt!KiSystemServiceCopyEnd, which makes the call to the system service, in this case nt!NtWaitForSingleObject. The trap frame is the first stack frame hence function on the kernel stack and begins at the rsp (Child-SP) of the function nt!KiSystemCall64 and starts with the homespace that it allocates for the next function. The first instruction of nt!KiSystemCall64 is swapgs to swap gs with the gs in IA32_KERNEL_GS_BASE, and then it stores rsp into gs:10h, which is the UserRsp field in the KPCR and then stores gs:1A8h into rsp, which is the RspBase field in the KPRCB, this is clearly swapping the user rsp with the kernel rsp for the thread, which is cached in the hyperthread that currently has that thread running's KPRCB. It then starts building the trap frame, where the rsp is currently pointing to offset 0x190 in the trap frame by definition (the byte after the end of the frame), starting with pushing SS, because the CPU doesn't do this when you use syscall instead of int 0x2e.

使用基本指针时,陷阱帧将比其上方帧的 ebp 小8(减去 ebp 和返回地址,每个4字节)),因此 nt!NtWaitForSingleObject

When a base pointer is used, the trap frame would be at 8 less than the ebp of the frame above it (subtract ebp and return address, 4 bytes each), so the frame of nt!NtWaitForSingleObject

异常具有一个异常帧,该异常帧使用 nt!KiExceptionDispatch 存储非易失性寄存器,例如,在 nt!KiBreakpointTrap 之后创建陷阱帧(并建立陷阱帧)与系统调用不同,因为处理器将 SS:RSP RFLAGS CS:RIP ErrorCode 推入堆栈用于用户模式异常,将 RFLAGS CS:RIP ErrorCode 放入内核模式异常的堆栈,并且不保存/从KPCR/KPRCB加载任何 rsp ). nt!KiExceptionDispatch 创建一个异常帧 KEXCEPTION_FRAME ,该异常帧从堆栈跟踪中的下一个 rsp 开始,并且 EXCEPTION_RECORD 立即出现之后,在堆栈上的总大小为 1D8 ,因此您会看到 nt!KiExceptionDispatch <的堆栈指针之间的 1E0 有所不同./code>和 nt!KiBreakpointTrap ,当您将返回地址包括到 nt!KiBreakpointTrap 时.

Exceptions have an exception frame that stores the non-volatile registers using nt!KiExceptionDispatch, after nt!KiBreakpointTrap for instance creates the trap frame (and the trap frame building different to a system call because the processor pushes SS:RSP RFLAGS, CS:RIP and ErrorCode to the stack for a user-mode exception and RFLAGS, CS:RIP and ErrorCode to the stack for a kernel-mode exception, and does not save/load any rsp from the KPCR / KPRCB). nt!KiExceptionDispatch creates an exception frame KEXCEPTION_FRAME beginning at the next rsp in the stack trace and the EXCEPTION_RECORD is immediately after above that on the stack, with a total combined size of 1D8, so you'll see a difference of 1E0 between the stack pointers of nt!KiExceptionDispatch and nt!KiBreakpointTrap when you include the return address to nt!KiBreakpointTrap.

陷阱帧存储了所有可以被内核破坏的寄存器在内核中称为易失性寄存器的函数数组期间,因为保证了易失性寄存器在系统调用或异常期间不会更改.它不会保存非易失性寄存器,因为在内核中调用的函数会保存它们使用的所有非易失性寄存器,因此,当它返回该函数以执行 sysret 时,所有的非易失性寄存器将是之前的值.您需要一个用于异常的异常框架,因为它需要在执行堆栈展开的函数中知道展开异常时所包含的非易失性寄存器的内容,以便可以展开堆栈.在堆栈中的 nt!KiDispatchException 中创建一个 CONTEXT 结构,以表示发生异常时的完整上下文,这是非易失性和易失性寄存器状态的组合.

The trap frame stores all registers that can be clobbered by the kernel during the array of functions that are called in the kernel, called volatile registers, because the volatile registers are guaranteed not to be changed during a syscall or an exception. It does not save non-volatile registers because the functions that are called in the kernel save any non-volatile registers they use, so by the time it returns to this function to perform a sysret, all the nonvolatile registers will be the value they were before. You need an exception frame for exceptions because it needs to know in the function that performs the stack unwinding what the nonvolatile registers contained at the time of the exception so that the stack can be unwound. A CONTEXT structure is created on the stack in nt!KiDispatchException to represent the full context at the time of the exception, which is a combination of the nonvolatile and volatile register state.

在交换线程上下文时,您还需要另一个异常帧,以便在线程关闭时保存非易失性寄存器的状态,因此这将是 nt!KiSwapContext 中的状态,这样就可以在切换回线程时将其恢复.不需要保存volatile状态,因为它假定对

You also need another exception frame when swapping thread contexts in order to save the state of the nonvolatile registers at the time the thread is switched out, so this will be the state in nt!KiSwapContext, so this can be restored when the thread is switched back in. The volatile state does not need to be saved, because it's assumed that the call to SwapContext discards all volatile registers. The trap frame created when entering kernel mode in the first place will be used to restore the volatile register context to the thread when it returns to user mode.

有趣的事实:对于断点异常 INT 3 ,CPU会在断点指令而不是断点指令结束后开始推送 rip 减少地址,以便堆栈调用站点的顶部是陷阱帧中的 rip ,而不是CPU推送的返回地址.

Fun fact: for a breakpoint exception INT 3, the CPU pushes the rip starting after the end of the breakpoint instruction and not the breakpoint instruction which means that windows needs to decrement the address so the top of the stack callsite is the rip in the trap frame and not the return address pushed by the CPU.

这篇关于儿童EBP vs儿童SP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆