The spy++ is showing the wndproc in your screen shot (it is probably subclassed; you may need to trace but wndproc is shown in your screenshot as
361c9880 I don't know what the command is in x64 dbg but if you were on ollydbg you simply do ctrl+g (goto) key in the address as shown in spy++ and break and log the messages for filtering.
A screen shot of calc.exe -> backspace button windows wndproc in comctl32.dll (32 bits and 64 bits shouldn't matter much on concept level)
An entry by raymond chen talks about cookies being returned instead of wndproc:
If all else fails assemble GetWindowLongPtrW in place to fetch the actual WndProc
- suspend the process (f12 or esc)
- use ctrl+g to goto user32.GetWindowLongPtrW
- right click set new origin here (save the rip prior this)
- save the state of register somewhere
- modify rcx and plop the handle into rcx (which was b01c8 in your screen shot)
- use the latest window handle as shown by spy++
- for the existing session do not put 0xb01c8
- modify edx to hold -4 (index of GWLP_WNDPROC)
- step through the Function
- before the function returns rax should hold the actual WndProc
- save or set a bp on the Wndproc
- restore registers and rip to pristine state and continue exploring
I downloaded x64dbg and ran 64 bit calc.exe spy++ 32 bit doesn't show wndproc. I cooked a script to alloc a page in process memory of calc.exe and assembled a detour using the script language and fetched the actual WndProc.
A screenshot below:
The debuggee must be in a paused state.
The script allocates memory in debuggees address space using alloc; after tabbing once the status bar should show the newly allocated address.
Also the variables $lastalloc $result should hold the newly allocated memory address; if you do
d address a bunch of 00 00 should stare at you.
- confirm the allocation
- if the memory is allocated tab one step in the script
- push rcx should be assembled in the newly allocated address
- use d address or d $lastalloc to confirm
- like wise assemble all the instruction
- use the proper handle value in ecx (stale or reused window handles may provide incorrect information confirm you assemble mov rcx , HWND right
- now you need to ensure you put the right address in eax the address should be of user32.GetWindowLongPtrW
- assemble all the cleanup instructions
- one you have done this
- save the existing rip some where (write it down in a paper)
- right click and select the first instruction in the newly allocated address and set it as origin ( new origin here) the rip will be changed to the newly allocated address
- hit f8 and execute the instructions on by one
- when call eax is done eax will hold the Wndproc
- save this (write it in paper)
- execute the cleanup instruction
- hit ctrl+g and enter the old RIP
- right click -> new origin here ( RIP will now point to the old value when you paused the debuggee
That is it; now you have Wndproc in a paper and you have returnd to the original state.
This is a detour (making an intentional bypass in the code flow of debuggee to do some extra work and return back to the place where bypass was done as if nothing was done to continue the original flow).
Use bp to set a breakpoint in the wndproc you have on paper.