Introduction
Debugging is a very important skill when it comes to software development and every software developer would have had times where they had to either debug something they wrote or something that was written by someone else. Depending on the platform you're working on, there are a bunch of tools that can help you with debugging with windbg being one of the most popular debugging tools for Windows. Windbg is something that I use heavily during my day to day work and I feel it's one of the most powerful tools ever written for Windows. There are many tutorials out there that give you an introduction to debugging with windbg, so this series will not focus on that. Instead, we will go through a few examples of how you can use windbg to do some reverse engineering and hacking and in the process, understand more about the different things you can do with windbg. While this particular part focuses on hacking Notepad through windbg, you can use similar techniques to hack something else. In future parts, I will show some advanced hacking and reverse engineering including some kernel mode examples.
Background
While I will try to go over every single debugging command used here, this tutorial doesn't aim to be a starting point for learning windbg or learning assembly. Some familiarity with x64 assembly and windows programming is assumed. I use the word "windbg" to refer to the GUI tool but the same commands apply even if you're using CDB or NTSD.
Hacking Notepad
In most of the windbg tutorials I have seen, Notepad seems to be the favorite demo application so for part 1, we will stick with that. The behavior we are trying to hack here is something you might have often seen and it annoys me a lot when I run into it. I saw a similar demo a couple of years back when someone else was doing it but I don't remember how they did it so I re-tried the same myself.
Let's say you open Notepad and then you click on "File->open" which opens up a dialog box to select the file you wish to open. You might have noticed while this dialog box is open, you cannot bring to the foreground the actual editing window. Clicking on the editing area throws an alert sound without bringing that window in foreground.
The rest of this tutorial will focus on how we can hack Notepad using windbg to change this behavior and actually keep switching back and forth between the two windows while both of them are open simultaneously.
The first step is to open Notepad from windbg so we can control and change its behavior. You can start windbg and then either choose File -> Open Executable -> <path to notepad.exe> or File->Attach to a process->notepad.exe if notepad.exe is already running.
The difference between these two options is in the first one (open executable), the debugger breaks in before the actual process starts executing its main
routine. The debugger application basically calls CreateProcess
API with a special flag that gives it control back after the application has been loaded in the memory but before it actually starts executing. In the second option (Attach to process), the debugger calls DebugActiveProcess
API which attaches the debugger to the process which is already running and breaks-in.
How exactly the debugger communicates with the process being debugged is outside the scope of this tutorial but curious folks can read about it in the Advanced Windows Debugging book.
Once we have broken in, the next logical step is to first find out what is the function we want to hack. For example, what is the function that gets called when we click the open dialog box from the File Menu. For doing this, we will need symbols for notepad.exe and luckily, the Microsoft public server does have public symbols for it.
You can use the .symfix
command to point the symbol path to the Microsoft public server and then do a .reload
to actually load the public symbols.
0:011> .symfix
0:011> .reload
Without knowing the exact function name, we don't know what to search for but we can make an educated guess. If you were the developer writing the code for Notepad, what would you name the function that gets called when you click the open dialog box?. Well, I hope it atleast has the word open in it!
So using the x command, we can search for functions inside notepad.exe which has the word open in it. Here is what it looks like:
0:011> x notepad!*open*
00007ff7`0307182c notepad!ShowOpenSaveDialog (<no parameter info>)
00007ff7`03092310 notepad!<wbr />pszEDPFileOpenErrorHeader = <no type information>
00007ff7`0308a6f8 notepad!_imp_GetOpenFileNameW = <no type information>
00007ff7`030923d8 notepad!szOpenCaption = <no type information>
00007ff7`0308b190 notepad!CLSID_FileOpenDialog = <no type information>
00007ff7`03074860 notepad!NpOpenDialogHookProc (<no parameter info>)
00007ff7`0308a890 notepad!_imp_OpenSemaphoreW = <no type information>
00007ff7`0308aad0 notepad!_imp_OpenClipboard = <no type information>
00007ff7`0308ace0 notepad!_imp_OpenPrinterW = <no type information>
00007ff7`030842d4 notepad!TraceFileOpenComplete (<no parameter info>)
00007ff7`03094ac0 notepad!szOpenFilterSpec = <no type information>
00007ff7`0308a688 notepad!_imp_RegOpenKeyExW = <no type information>
00007ff7`03092300 notepad!g_ftOpenedAs = <no type information>
00007ff7`0307199c notepad!InvokeOpenDialog (<no parameter info>)
00007ff7`03093438 notepad!g_isFileDragOpen = <no type information>
00007ff7`03092308 notepad!pszEDPFileOpenError = <no type information>
00007ff7`0308a650 notepad!_imp_OpenProcessToken = <no type information>
The first function highlighted here in bold, ShowOpenSaveDialog
, looks exactly like something we are looking for!
Let's see if this is indeed the one that gets called when we click open. In order to verify this, we will put a breakpoint on it.
0:011> bp notepad!ShowOpenSaveDialog
0:011> g
The first command bp
is actually putting the software breakpoint on our function (int 3
) and the next command g
, which stands for go, releases the debugger control and Notepad starts executing again.
We now click on File->Open and you should see Notepad freeze when you do this. The debugger will have broken-in saying it hit our breakpoint and we can check the call stack at this time.
Breakpoint 0 hit
notepad!ShowOpenSaveDialog:
00007ff7`0307182c 48895c2408 mov qword ptr [rsp+8],
rbx ss:00000073`74d2f310=<wbr />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!<wbr />UserCallWinProcCheckWow+0x2c1
05 00000073`74d2fb80 00007ff7`03073bdb USER32!DispatchMessageWorker+<wbr />0x1c3
06 00000073`74d2fc10 00007ff7`03089333 notepad!WinMain+0x27f
07 00000073`74d2fd10 00007fff`68ea3034 notepad!__mainCRTStartup+0x19f
08 00000073`74d2fdd0 00007fff`69073691 KERNEL32!BaseThreadInitThunk+<wbr />0x14
09 00000073`74d2fe00 00000000`00000000 ntdll!RtlUserThreadStart+0x21
Great! We found the function of interest relatively easily, but now what? Once we have this, what do we do with it?
If you're familiar with Windows programming, you will know that Windows heavily uses the concept of objects and handles. Whenever you want to use an object, you always get a handle to it. If we think of the open dialog box and the editor area as two separate "window", then they would be two objects. Since we can't bring the editor window in the foreground till this dialogbox
is open, that also suggests a parent-child relationship. At this stage, all this is speculation since we don't have the source code for Notepad but if our speculation is true, then this function will somehow know that its parent window is the editor window.
So based on this logic, my guess here is that this function takes a handle to the parent window as an argument to establish this relationship.
Without the private symbols, you can't dump local
variables or parameters passed using the dv
command but if you know your Microsoft x64 calling convention (https://msdn.microsoft.com/en-us/library/ms235286.aspx), you would know that the first 4 parameters are passed in the rcx
, rdx
, r8
and r9
registers. So let's dump these using the r
command.
0:000> r
rax=0000000000000000 rbx=0000000000000000 rcx=000000000004094e
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=<wbr />0000000000000000
I have highlighted the 4 registers in the output above. If you have used Window
objects before which are represented by HWND
(Handle to Window) and looked at the values it can have, you would instantly notice that only the rcx
register has something that even remotely resembles a HWND
.
But for the sake of completeness, let's assume you don't know what a handle value is actually supposed to look like.
To check this, we can close this instance of Notepad, and then open windbg again and this time, choose Open executable option pointing to the Notepad path. We want to see what's the HWND
value that gets returned when we actually create a window in Notepad. So to do this, we will again use our friend x
to search for functions that might resemble creating a window.
0:000> x notepad!*create*window*
00007ff7`0308a6c8 notepad!_imp_<wbr />CreateStatusWindowW = <no type information>
00007ff7`0308ab78 notepad!_imp_CreateWindowExW = <no type information>
_imp_
represents that the actual function is inside the address of this _imp_CreateStatusWindowW
. So we can dump the pointer value inside _imp_CreateStatusWindowW
and unassemble it to get the actual function.
0:000> x notepad!*create*window*
00007ff7`0308a6c8 notepad!_imp_<wbr />CreateStatusWindowW = <no type information>
00007ff7`0308ab78 notepad!_imp_CreateWindowExW = <no type information>
0:000> dq 00007ff7`0308ab78
00007ff7`0308ab78 00007fff`664a4710 00007fff`664c5780
00007ff7`0308ab88 00007fff`664ae980 00007fff`664a4660
00007ff7`0308ab98 00007fff`664b8e80 00007fff`664b5710
00007ff7`0308aba8 00007fff`664b8540 00007fff`664b3f10
00007ff7`0308abb8 00007fff`664c7c20 00007fff`664a26e0
00007ff7`0308abc8 00007fff`664a8800 00007fff`664c8410
00007ff7`0308abd8 00007fff`664d06c0 00007fff`664d0b20
00007ff7`0308abe8 00007fff`664c1500 00007fff`664cffb0
0:000> u 00007fff`664a4710
USER32!CreateWindowExW:
00007fff`664a4710 4c8bdc mov r11,rsp
00007fff`664a4713 4881ec88000000 sub rsp,88h
00007fff`664a471a 33c0 xor eax,eax
00007fff`664a471c 6689442478 mov word ptr [rsp+78h],ax
00007fff`664a4721 89442470 mov dword ptr [rsp+70h],eax
00007fff`664a4725 c744246800000040 mov dword ptr [rsp+68h],40000000h
00007fff`664a472d 89442460 mov dword ptr [rsp+60h],eax
00007fff`664a4731 488b8424e8000000 mov rax,qword ptr [rsp+0E8h]
If you check MSDN for CreateWindowExW
, you will see that it returns HWND
, just what we are looking for! So let's put a breakpoint on this function and see what is the return value we get. Once we hit our breakpoint, we need to let this function execute and return back to the caller so we can use gu
to go back one level in the stack to the function which actually called our function and then check the return value at this stage.
The return value itself is stored in the rax
register which again is part of the calling convention.
0:000> g
ModLoad: 00007fff`66f10000 00007fff`66f3d000 C:\WINDOWS\System32\IMM32.DLL
Breakpoint 0 hit
USER32!CreateWindowExW:
00007fff`664a4710 4c8bdc mov r11,rsp
0:000> k
# Child-SP RetAddr Call Site
00 00000083`2f57f958 00007fff`6670afbe USER32!CreateWindowExW
01 00000083`2f57f960 00007fff`666cefb6 combase!InitMainThreadWnd+0x56
02 00000083`2f57f9d0 00007fff`666ce004 combase!ThreadFirstInitialize+<wbr />0x12a
03 00000083`2f57fa30 00007fff`666cecd6 combase!_CoInitializeEx+0x158
04 00000083`2f57fb30 00007ff7`03073ab1 combase!CoInitializeEx+0x36
05 00000083`2f57fb80 00007ff7`03089333 notepad!WinMain+0x155
06 00000083`2f57fc80 00007fff`68ea3034 notepad!__mainCRTStartup+0x19f
07 00000083`2f57fd40 00007fff`69073691 KERNEL32!BaseThreadInitThunk+<wbr />0x14
08 00000083`2f57fd70 00000000`00000000 ntdll!RtlUserThreadStart+0x21
0:000> gu
combase!InitMainThreadWnd+<wbr />0x56:
00007fff`6670afbe 48890563af2000 mov qword ptr [combase!ghwndOleMainThread (00007fff`66915f28)],
rax ds:00007fff`66915f28=<wbr />0000000000000000
0:000> r@rax
rax=00000000000d071c
As we see, the highlighted value represents an HWND
value and from the previous dump of the ShowOpenSaveDialog
routine, we can verify that only rcx
indeed resembles something like an HWND
value.
So how do we break this parent-child relationship between the two window objects? One easy way would be just clear the rcx
register so the HWND
value is 0
. Of course, this may lead to a crash or not, but we don't have anything better to try at this stage.
Here is how we can overwrite the register and confirm it is actually 0
.
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=<wbr />0000000000000000
We let the program resume now by hitting g
and now if you click on the editor window while this dialog box is open, voila! It worked! Assuming you followed the steps listed here properly, you should be able to switch back and forth between the two windows now.
But wait! We don't want to keep setting this breakpoint everytime and changing the value of rcx
everytime we open the dialogbox
. Can we change the code to do this automatically for us from the debugger? You bet we can.
So for this, we need to make sure when the caller calls ShowOpenSaveDialog
, it zeroes out the rcx
register. The caller in this case is the InvokeOpenDialog
function. Let's look at the assembly code just before it calls ShowOpenSaveDialog
. You can use the ub
command for this which unassembles backwards from a given address.
0:000> k
# Child-SP RetAddr Call Site
00 00000049`9cb1f288 00007ff7`03071aeb notepad!ShowOpenSaveDialog
01 00000049`9cb1f290 00007ff7`030721fa notepad!InvokeOpenDialog+0x14f
02 00000049`9cb1f2f0 00007ff7`030738d6 notepad!NPCommand+0x4a2
03 00000049`9cb1f670 00007fff`664b6d41 notepad!NPWndProc+0x726
04 00000049`9cb1f970 00007fff`664b6713 USER32!<wbr />UserCallWinProcCheckWow+0x2c1
05 00000049`9cb1fb00 00007ff7`03073bdb USER32!DispatchMessageWorker+<wbr />0x1c3
06 00000049`9cb1fb90 00007ff7`03089333 notepad!WinMain+0x27f
07 00000049`9cb1fc90 00007fff`68ea3034 notepad!__mainCRTStartup+0x19f
08 00000049`9cb1fd50 00007fff`69073691 KERNEL32!BaseThreadInitThunk+<wbr />0x14
09 00000049`9cb1fd80 00000000`00000000 ntdll!RtlUserThreadStart+0x21
0:000> ub notepad!InvokeOpenDialog+0x14f
notepad!InvokeOpenDialog+<wbr />0x133:
00007ff7`03071acf 8bd8 mov ebx,eax
00007ff7`03071ad1 85c0 test eax,eax
00007ff7`03071ad3 782c js notepad!InvokeOpenDialog+<wbr />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)
We see that rcx
is getting the value from r14
which is highlighted above. What if instead of this mov
instruction, we had something like xor ecx,ecx
?. We just need to zero out the ecx
register since HWND
is a type of HANDLE datatype
which are 32 bit values in practice even though it is defined as a PVOID
. So essentially, we need something like this:
mov rdx,qword ptr [rbp+38h]
xor ecx,ecx
call notepad!ShowOpenSaveDialog
We can use the a
(assemble) command in windbg to achieve this.
0:000> a 00007ff7`03071ae3
00007ff7`03071ae3 xor ecx,ecx
xor ecx,ecx
00007ff7`03071ae5 nop
nop
00007ff7`03071ae6
0:000> ub notepad!InvokeOpenDialog+0x14f
notepad!InvokeOpenDialog+<wbr />0x135:
00007ff7`03071ad1 85c0 test eax,eax
00007ff7`03071ad3 782c js notepad!InvokeOpenDialog+<wbr />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 31c9 xor ecx,ecx
00007ff7`03071ae5 90 nop
00007ff7`03071ae6 e841fdffff call notepad!ShowOpenSaveDialog (00007ff7`0307182c)
When using the a
command, we must take care that the next instruction that follows the one which we are modifying, starts at the original offset. For example, the mov
instruction before started at ae3
offset and the call instruction was at ae6
. When we assemble xor ecx,ecx
at ae3
, it is a 2 byte instruction so we need to pad another 1 byte instruction at ae5
. For this, we just use the nop
instruction which doesn't really do anything.
Now using the ub
command again, we see that our code is successfully modified with what we need. We can now disable all the breakpoints and resume Notepad. Since we didn't change rcx
this time in the current invocation, we will not be able to switch back and forth between the windows yet. We can close this open dialog box this time and from the next subsequent times whenever we open it, we should be able to switch back and forth between the two windows now.
You should note that this code modification is done in the memory. So the actual binary doesn't get affected. Whenever you close Notepad and restart it, this hack won't be there so you will have to do it again if you wish to.
Conclusion
So here, we saw how we can use windbg to understand how the program execution works, search for functions inside an application and even change the behavior of an application whose source code we don't have! Hopefully, this gives you a greater sense of appreciation for windbg to see the different things you can do with it. In the next part, we will pick another application and do some more advanced hacking.
History
- 10th February, 2019: Initial version
Click this link to read the next article.