In this section, we will be modifying notepad's ASM code to call our NotePlug DLL function, as well as a NotePlugCmd function which will be written in the next section.
[~] OllyDbg
- Debugger/disassembler
[~] LordPE
- For modifying notepad's import table
Add the NotePlug and NotePadCmd functions to notepad's import table using LordPE and be sure to note their addresses (in my case 0101409E and 010140A2). Now we need to find a place to call our function from, but it must be called after the window has been created. Run notepad in Olly, press F12 to pause and single step (F8) until you return from the User32 functions and you will be in a loop at 010029B3:
This is the standard GetMessage loop used for pretty much all Win32 programs which translates and dispatches window messages to the WndProc function. Notice also that the TranslateAccelleratorW function is passed the main window handle which is stored at 01009830. If you search for CreateWindowExW and look at the instance that creates the window with the 'Notepad' class (the main window), you will see that the window handle is also stored at offset 100A4A4:
Hijacking the execution flow to our imported function just before the loop at 010029B3 would be ideal, because notepad's window has been sucessfully created at that point and we are free to modify the menu. Note immediately above the loop there is an unconditional jump directly to the GetMessage call ('CALL EDI' at 01002A19 is a call to GetMessage):
This is ideal because the jump is taken once, immediately before entering the GetMessage loop. Unfortunately, it is a JMP SHORT instruction, and we will need to use a JMP LONG to get to our code which will overwrite the first instruction of the GetMessage loop (CMP DWORD PTR SS:[EBP-1C],50). We will need to add that overwritten instruction to our new code after having called NotePlug, but will also need to modify the jump at the bottom of the loop (JNZ SHORT 010029B3) because it will be pointing to this first instruction which will be overwritten with our new code. This also is a JMP SHORT instruction, so jumping to our replacement code will create the same problem as before of overwriting another instruction. We will have to add two jumps immediately after GetMessageW is moved into EDI (010029AF) and modify the loop's bottom jump to point to our second jump which in turn points to our new code. This also requires overwriting the MOV EBX,EAX instruction at 010029AF, so we will also have to add that instruction to our new code before returning the execution flow to its original place. Below is the new code to be added, the differences between the original and modified GetMessage loop, and an execution flow chart to better visualize the new execution flow:
Add new code at 0100874A:
Original GetMessage loop code:
Modified GetMessage loop code (added jumps at 010029AF and 010029B4 and modified conditional jump at 01002A1D):
Execution flow chart:
010029AF JMP notepad6.0100874A <---Used to jump directly to 01002A12 | |-----> 0100874A PUSHAD 0100874B PUSHFD 0100874C PUSH DWORD PTR DS:[1009830] <---- Push the main window handle 01008752 CALL DWORD PTR DS:[01001409E] <---- Call our function 01008758 POP EAX 01008759 POPFD 0100875A POPAD 0100875B MOV EBX,EAX <---- Restore this instruction which we wrote over 0100875D JMP notepad6.01002A12 <---- Jump back to the original code at 01002A12 | |------------------------------------------------------------ 010029B4 JMP notepad6.01008762 | | | |-----> 01008762 CMP DWORD PTR SS:[EBP-1C],50 ----- | 01008766 JNZ notepad6.010029CC | Restore original code | 0100876C JMP notepad6.010029B9 ----- | | | 010029B9 PUSH ESI | 010029BA PUSH ESI | 010029BB PUSH 8001 | 010029C0 PUSH DWORD PTR DS:[1009830] | 010029C6 CALL DWORD PTR DS:[<&USER32.PostMessageW> | 010029CC MOV EAX,DWORD PTR DS:[100983C] | 010029D1 CMP EAX,ESI | 010029D3 JE SHORT notepad6.010029E4 | 010029D5 LEA ECX,DWORD PTR SS:[EBP-20] | 010029D8 PUSH ECX | 010029D9 PUSH EAX | 010029DA CALL DWORD PTR DS:[<&USER32.IsDialogMess> | 010029E0 TEST EAX,EAX | 010029E2 JNZ SHORT notepad6.01002A12 | 010029E4 LEA EAX,DWORD PTR SS:[EBP-20] | 010029E7 PUSH EAX | 010029E8 PUSH DWORD PTR DS:[100A6D8] | 010029EE PUSH DWORD PTR DS:[1009830] | 010029F4 CALL DWORD PTR DS:[<&USER32.TranslateAcc> | 010029FA TEST EAX,EAX | 010029FC JNZ SHORT notepad6.01002A12 | 010029FE LEA EAX,DWORD PTR SS:[EBP-20] | 01002A01 PUSH EAX | 01002A02 CALL DWORD PTR DS:[<&USER32.TranslateMes> | 01002A08 LEA EAX,DWORD PTR SS:[EBP-20] | 01002A0B PUSH EAX | 01002A0C CALL DWORD PTR DS:[<&USER32.DispatchMess> | 01002A12 PUSH ESI <-----------------------------------------------------------------------| 01002A13 PUSH ESI 01002A14 LEA EAX,DWORD PTR SS:[EBP-20] 01002A17 PUSH ESI 01002A18 PUSH EAX 01002A19 CALL EDI 01002A1B TEST EAX,EAX 01002A1D JNZ SHORT notepad6.010029B4 -----------> Jump back up to 010029B4 instead of 010029B3
This takes care of calling NotePlug when notepad first loads, and we still maintain the original code for the GetMessage loop. Next we need to intercept all WM_COMMAND messages sent to the program and create a function which will allow each plugin to process the message.
The function which will be be controlling WM_COMMAND messages for notepad's plugins will be called NotePadCmd. We have not written the code for this function yet, but I think it is easier if you understand the assembly code first.
In windows programs, all messages are processed by a WndProc function, so in order to catch all WM_COMMAND messsages, let's find notepad's WndProc function. Do a search in Olly for 'SHR EAX,10'. This should land you in the middle of the main case-switch statement in the WinProc function; the case that pertains to WM_COMMAND is a few instructions below:
If it is a WM_COMMAND message, the program jumps to 0100391B where it pushes three arguments and calls a function before returning to the case-switch loop:
0100391B PUSH EDI ; /Arg3 - Unknown value 0100391C PUSH DWORD PTR SS:[EBP+10] ; |Arg2 - ID number of menu option 0100391F PUSH DWORD PTR SS:[EBP+8] ; |Arg1 - Handle to notepad's main window 01003922 CALL notepad.01002B87
Since we need to know the ID number and possibly the window handle (some plugins may require the window handle, so we will pass it on just in case), replace the 'CALL 01002B87' with 'JMP 01008772', and let's call our NotePadCmd function. At address 01008772, add the following code:
01008772 PUSHAD <----- Save the register values 01008773 PUSHFD 01008774 PUSH EDI <----- Push the three arguments onto the stack for our function 01008775 PUSH DWORD PTR SS:[EBP+10] 01008778 PUSH DWORD PTR SS:[EBP+8] 0100877B CALL DWORD PTR DS:[010140A2]<---- Call our function 01008781 TEST EAX,EAX <---- Check the return value 01008783 JNZ notepad9.010034BD <---- If the return value is not zero, jump back into the case-switch loop 01008789 POP EAX <---- If the return value is zero, restore the stack and registers 0100878A POP EAX 0100878B POP EAX 0100878C POPFD 0100878D POPAD 0100878E CALL notepad9.01002B87 <---- Call the original function 01008793 JMP notepad9.01003927 <---- Jump back to notepad's original code
Note that we test the return value to determine if notepad should continue processing the WM_COMMAND message, or if it should return to the case-switch loop. Remember that this was one of the four original objectives; we want each plugin to be able to control whether or not notepad and the remaining plugins will continue to process the message.
We now have calls to both NotePlug and NotePadCmd, and the assembly coding is complete at this point. All that remains is to complete the NotePlug and NotePadCmd functions, and create a standard structure for plugin DLLs.
Copyright ©2006 craigheffner.com