📜 ⬆️ ⬇️

"Pumping" notepad.exe

image

What is your association with the F5 key? Updating the page in the browser? Copying a file from one directory to another? Running an application from Visual Studio? But the authors of notepad.exe approached this issue in a rather original way - pressing the F5 key adds the current date and time to the place where the cursor is pointing at at that moment. Everything would be cool if notepad.exe were such a popular and quite natural for most text editors feature, as re-reading the contents of the current file, which, it would seem, should be assigned to F5 / Ctrl-R or some other standard hot key

We can wait while it is being implemented by Microsoft, choose another text editor (this is not the only restriction on the functionality of the standard notepad.exe) or ... Pick up the disassembler, debugger and editor of PE files.
')
How was the process, and what came of it, read under the cut (carefully, a lot of screenshots ). Before reading this article, I also strongly recommend that you review the previous ones .

In order not to deal with the same inconvenience that we encountered in the previous article , let's turn off the use of ASLR first . According to the wiki, ASLR (Address space layout randomization) is a technology that makes it possible to randomly change the location of important structures in the address space of the process, namely, the image of the executable file, loadable libraries, heaps and stacks. It was because of her last time restarting the application and led to a change in addresses already found by us. If you are using Windows XP or an older OS, you can easily skip what is described in the next few paragraphs, because ASLR was not yet at that time.

You can disable the use of ASLR globally (to do this, add / edit the value of the “MoveImages” option stored in the registry at “HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ Memory Management” to make it zero) and locally i.e. for a particular executable file. The latter option looks more attractive, especially if we are not talking about a virtual machine, but about a real system, so let's dwell on it.

Copy notepad.exe to any directory other than "% WINDIR% \ System32", download, unzip and launch PE Tools , press Alt-1 and select notepad.exe that was previously copied:

image

Click on the “Optional Header” button and look at the DLL Flags field, which in our case is 0x8140:

image

The value in this field is the result of performing the “OR” bit operation for the constants listed in the official MSDN documentation . It is easy to see that our binary has the following characteristics:

IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
0x8000
The image is terminal server aware

IMAGE_DLLCHARACTERISTICS_NX_COMPAT
0x0100
The image is compatible with data execution prevention (DEP)

IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
0x0040
The DLL can be relocated at load time

Notice the last meaning? Well, that's exactly what interests us. Change 0x8140 to 0x8100, click "Ok" in both windows and start debugging.

What stages can conditionally divide our notepad.exe patching?


Open notepad.exe in OllyDbg and proceed to the first stage.

You can approach the search for the address where the path to the current file is stored from several sides at once. You can, for example, find a procedure that deals with opening a file (most likely, if successful, it saves the path to the file to some address), or you can look at the implementation of the file saving algorithm (obviously, he should know either the handle of the current file or way to him). I propose to stay on the second version.

Hoping that the file opens again every time it’s saved, we set breakpoints on the calls to the WinAPI CreateFileW function:

image

Press Ctrl-S, select the file name (in my case it is “C: \ helper.txt”) and stop at the following place:

image

Let's see where and with what arguments we were called:

image

If you look at what the address passed as the second argument indicates (the right-click on the line with this argument -> Follow address in stack), then we will see exactly our path:

image

Let's look at the code before the call of the procedure we are examining in order to understand where this address came from and how we got it:

image

As you can see, the address where the path to the file is stored is contained in EBP-8 . Let's press Ctrl-S again and see where we go this time (after all, now the program already knows the path to the file, which can change the way the application works):

image

So, we ended up on the same breack as before, but called us from another place:

image

This time the address containing the path to the file is stored in the EBX register. Since the beginning of the current case block (note the comment with several instructions before the allocated space), the value of this register does not change, which means that you need to look for the original address somewhere before. We look at which instructions refer to the beginning of this case block (left-click at 0x01004D5D -> Ctrl-R):

image

Once such a call is only one, we jump onto it by pressing the Enter key and immediately see where this address appears in EBX :

image

So, we realized that at the address 0x0100CAE0 the path to the current file is stored. What's next? And then we must find the procedure responsible for reading the contents of the file.

Obviously, it will also call CreateFileW (instead, we could intercept the call to the GetOpenFileName function, but it is not in the list of intermodule calls — apparently, the Common Item Dialog API, which is recommended on MSDN, is used instead). Press Ctrl-O, select any file (I chose the same one) and, not having time to double-click with the mouse, find ourselves on the breakpoint at 0x01006E8C :

image

We do the same thing several times before removing this breakpoint and hoping for the rest. And the truth is, after the bryak was removed to the address specified earlier, we were still able to double-click on the file of interest to us, as a result of which the breakpoint worked in a completely different place:

image

So, our task is to find out how and exactly which procedure should be called in order to successfully re-read the file of interest to us. We put bryak on the address from which we were called

image

, press F9, and ... It immediately works! Nothing, press F9 again, try to transfer the focus to the notepad.exe window and see that the bryak works again. Yes, what is it! Let's look at the beginning of the procedure that this CALL calls:

image

Pay attention to the only comment - judging by the number of processed values ​​and what we observe in practice, this procedure is used to respond to any action performed by the user, be it transferring the focus note.exe file to the window or opening a file. Apparently, after pressing Ctrl-O, the program does not perform any CALL , but only moves to the corresponding case block using the conditional branch operation. Let's remove this breakpoint, try again to open the file and find the one closest to the breakpoint that is in place of the call to CreateFileW , the instruction to which there is a call in the code. She was the instruction at the address 0x01004DF5 :

image

We put bryaki on both appeals, perform the same actions and find ourselves here:

image

Put the bryak on the beginning of this case, again open the same file and try to understand what is happening here:

;     EDI 01003ECC > \33FF XOR EDI,EDI ; Case 2 of switch 01001824 ;        ;   ,          01003ECE . 57 PUSH EDI 01003ECF . E8 90D7FFFF CALL notepad.01001664 ;    ; EAX == 1,     /    Save / Don't Save, EAX == 0,     Cancel 01003ED4 . 85C0 TEST EAX,EAX ;   Cancel,      ,    case 01003ED6 .^ 0F84 8ED9FFFF JE notepad.0100186A ;     0x100C00C  EAX    EBP-10 01003EDC . A1 0CC00001 MOV EAX,DWORD PTR DS:[100C00C] 01003EE1 . 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX ;          01003EE4 . 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 01003EE7 . 50 PUSH EAX ; /Arg2 01003EE8 . FF75 F4 PUSH DWORD PTR SS:[EBP-C] ; |Arg1 01003EEB . E8 31000000 CALL notepad.01003F21 ; \notepad.01003F21 ;       EBP-8       ; EAX == 0     0x800704C7     Cancel 01003EF0 . 8BF0 MOV ESI,EAX 01003EF2 . 3BF7 CMP ESI,EDI ;        01003EF4 . 0F8D FB0E0000 JGE notepad.01004DF5 01003EFA . 81FE C7040780 CMP ESI,800704C7 01003F00 . 0F85 DC0E0000 JNZ notepad.01004DE2 01003F06 > 3BF7 CMP ESI,EDI 01003F08 . 0F8D E70E0000 JGE notepad.01004DF5 01003F0E > 8B45 F0 MOV EAX,DWORD PTR SS:[EBP-10] 01003F11 . A3 0CC00001 MOV DWORD PTR DS:[100C00C],EAX 01003F16 . 56 PUSH ESI 01003F17 .^ E9 A2FCFFFF JMP notepad.01003BBE 

Now let's see which registers and addresses are used by the code at 0x01004DF5 , in order to understand what kind of “environment” is necessary for its correct operation:

image

Of course, this code refers to EBP-8 , which, as you remember, stores the path to the file being opened. In addition, the value of the EDI register is also important to it, which is used as arguments for the hTemplateFile and pSecurity parameters . The first we can get from the address 0x0100CAE0 , and in the designated parameters, you can simply pass a zero.

Now let's find the code responsible for handling the F5 key presses. To do this, I propose to put a breakpoint on calls to functions that are responsible for obtaining the current time. The most popular of them are GetSystemTime and GetLocalTime . The first one is not in the list of intermodular calls, but the second one is called from two places at once:

image

We put bryaki, press F5 and find ourselves here:

image

We jump into the place of calling the current procedure and get almost to the very beginning of another case block, which, obviously, is responsible for handling the F5 key:

image

Fine. We are looking for a place for our code cave and write (of course, addresses may vary):

 0100BEB3 33FF XOR EDI,EDI 0100BEB5 C745 F8 E0CA0>MOV DWORD PTR SS:[EBP-8],notepad.0100CAE0 ; UNICODE "C:\helper.txt" 0100BEBC A1 0CC00001 MOV EAX,DWORD PTR DS:[100C00C] 0100BEC1 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX 0100BEC4 ^ E9 2C8FFFFF JMP notepad.01004DF5 

We insert at the address 0x0100447B jump to our code cave:

image

Press F9, press F5 again and see the following picture:

image

As you can see, we fell somewhere in the depths of the CoTaskMemFree function. Pay attention to the argument passed to this function - yes, yes, this is the address of our string with the path to the file. Therefore, the memory for it must be allocated using CoTaskMemAlloc . The SHStrDup function, which creates a duplicate of the string passed to it, can help us with this, allocating memory for it with the help of CoTaskMemAlloc .

Restart notepad.exe and look for the address of the SHStrDupW function in IAT. To do this, we look at the call of any other WinAPI function in the module:

image

Consequently, the address of the GetDlgItemTextW function in IAT is 0x010012A4 . Jump on it and look for our SHStrDupW :

image

It turns out that its call can be issued in the form of a CALL DWORD PTR DS instruction : [010013B4] . Then we write the following code (error checking is omitted):

 0100BFA5 . 33FF XOR EDI,EDI 0100BFA7 . 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 0100BFAA . 50 PUSH EAX ; /pTarget 0100BFAB . 68 E0CA0001 PUSH notepad.0100CAE0 ; |Source = "C:\helper.txt" 0100BFB0 . FF15 B4130001 CALL DWORD PTR DS:[<&SHLWAPI.SHStrDupW>] ; \SHStrDupW 0100BFB6 . A1 0CC00001 MOV EAX,DWORD PTR DS:[100C00C] 0100BFBB . 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX 0100BFBE .^ E9 328EFFFF JMP notepad.01004DF5 

Open our file “C: \ helper.txt”, make sure that it is empty, edit and save it in another copy of notepad.exe, press F5 in the version we are debugging, and ... The file is updated!

Let's save our changes to the executable file. Make a right-click on the window CPU -> Copy to executable -> All modifications -> Copy all and see:

image

It turns out that we got out of the physical boundaries of the executable file. Let's take a look at the boundaries of sections in PE Tools (button "Sections")

image

and put our code cave in some other place. To get the upper “border” of the area for the “painless” patch, we need to fold the Virtual Offset of the .text section, where we are going to put our patch, its Raw Size and Image Base, i.e. Virtual Offset ( 0x00001000 ) + Raw Size ( 0x0000A800 ) + Image Base ( 0x01000000 ) = 0x0100B800 . Place it, for example, at 0x0100B6CF and try to save the changes again (right-click on the CPU window -> Copy to executable -> All modifications -> Copy all -> right-click in the window that appears -> Save file).

We check the resulting executable file for performance and make sure that everything behaves as expected.

Afterword


The purpose of this article is to once again demonstrate the possibility of adding your own functionality to existing programs, while not having the source codes. Now go back to your vims / emacs / Notepad ++ / etc, but remember - if you encounter a bug or pay attention to the absence of any functionality in the editor with a closed code, now you know what to do.

Thank you for your attention, and again I hope that the article was useful to someone.

Source: https://habr.com/ru/post/261507/


All Articles