²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² ²² ____ __ __ ²²ßÛ ²² / _/_ _ __ _ ___ ____/ /____ _/ / ²² ÛßÛ ²² _/ // ' \/ ' \/ _ \/ __/ __/ _ `/ / ²² Û Û ²² /___/_/_/_/_/_/_/\___/_/ \__/\_,_/_/ ²² Û Û ²² ____ __ __ ²² Û Û ²² / __ \___ ___ _______ ___ ___/ /__ ____ / /____²² Û Û ²² / /_/ / -_|_-</ __/ -_) _ \/ _ / _ `/ _ \/ __(_-<²² Û Û ²²/_____/\__/___/\__/\__/_//_/\_,_/\_,_/_//_/\__/___/²² Û Û ²² ²² Û Û ²² Web: http://www.ImmortalDescendants.com ²² Û Û ²² Author: Amante4 ²² Û Û ²² Date: 03/21/2000 ²² Û Û ²² Topic: Adding Password protection to ²² Û Û ²² Netscape to control the clearing ²² Û Û ²² or modification of the history. ²² Û Û ²² Level: Advanced ²² Û Û ²² ²² Û Û ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² Û Û ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ Û ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ
Hello, and welcome to my first essay on adding functionality to a program. I have to admit, this has been a challenge for me but it was well worth it. I have learned a great deal from doing this and hope you do too.
*************************** * Introduction ***************************
One thing that was brought to my attention was the fact that Netscape has no protection on the clearing and modifying of the history file associated with each user profile. I thought, wow wouldn't it be nice if these operations could be protected with a password. That way you can have another means for keeping track (to some degree) of what other people have been doing while on the internet. This presented a neat little project to test my skills in adding functionality into a program.
OK, lets see what things we'd like to protect and establish a baseline for what this essay is going to cover. First of all we know we are going to use a password protection. Here are the things we'd like to protect:
1) The "Clear History" button in Preferences. (Under Navigator subtopic) 2) Not allow manual deletions when you view the history. (Ctrl-H) 3) The number of days after which pages in history expire. (Under Navigator subtopic) 4) Not allow tampering/deletion of the history file on the disk.
That seems to do it. We'll also allow a facility to change the password in which we'll add a new button to a preferences dialog for this purpose.
I think it's wise to mention that I've decided to code the main part of the code to do the dialog boxes and password checking in C. That way I'm going to create a dll to add to Netscape. This will minimize the amount of patching needed in the existing exe's/dll's. This is key too because Netscape changes versions often and it would suck to have to patch thousands of bytes each time it changed. This method will also simplify our task to finding the locations to inject code, and injecting code to call our routines in the dll, then handle the return value correctly and carry on our merry way.
Another side note is that this is not intended for complete newbies. A moderate knowledge of the PE file format is suggested, as is a general knowledge of assembly language. Some reversing/cracking experience is also helpful.
*************************** * Tools Needed *************************** 1) First and foremost: A Brain 2) A Debugger, SoftICE 3) A Disassembler, W32Dasm and/or IDA 4) A Resource Editor, Borland Resource Workshop v4.5 I've actually used Symantec's Resource Studio 1.0 for a good bit of this work. Only because I've had problems with BRW of late. Not sure why???? 5) Hex Editor, HVIEW 6.x 6) ProcDump32 v1.6FINAL 7) API Reference 8) C Compiler 9) Spy32 - A windows spy tool 10) Netscape (This essay is being done on version 4.7)
*************************** * The DLL ***************************
As you know I've chosen to program the main protection routines in C. Please see the source code which has been provided as nspc.zip. This ZIP file also contains a precompiled dll as well. The source code is commented to a certain extent. I've leveraged a dialog box from Netscape to ask for the password. This dialog resides in the file resdll.dll in the Netscape directory. There's no need to add our own dialog when one already exists. The source code shows how to do this. I've added a custom dialog to the dll for changing the password because I don't think Netscape has one like this. As you may also see I'm using the registry to store the encrypted password.
The code provided should give you a basic idea of the structure of a dialog procedure and how to create dialog boxes both directly your own and indirectly from existing resources. The main thing here to notice is the ID numbers of the various controls of the dialog are used to communicate with the dialog. These ID's were once again found by looking at the various controls in our resource editor. You can look at the other examples of this in the source code.
In the end we end up with functions to export from the dll. I suggest you take a look at the two following essays on how to code a dll for windows:
1) http://www.immortaldescendants.com/database/essays/blackdruid/dll.txt Well, as of this writing our site is still down so maybe try our mirror sites at: http://ID2K.cjb.net//database/essays/blackdruid/dll.txt or http://ID2K.t500.net//database/essays/blackdruid/dll.txt 2) http://www.phys.uu.nl/~vdweerd/dread/cgi-bin/essay.cgi?using_dll
I'm exporting the functions I've created as Ordinals. That is strictly number based rather than name based. There are 2 reasons for this:
1) I don't want to have revealing string references in the code, nor do I want to add them to every file I need to patch. 2) It's much easier from an assembly point of view to simply push the ordinal number of the function you want then to push a pointer to a string when calling GetProcAddress.
Here is a list of the functions I've exported, their ordinal number, and a description of what they do. Function Name Ordinal Description ============================================================== ask_for_passwd 1 If password doesn't exist, prompts for first time password otherwise asks user to enter password. Returns 0 for success and NON Zero for failure
change_password 2 Changes an existing password. Returns 0 for success and NON Zero for failure
check_history_file 3 Checks to see if the history file for the current profile hasn't been modified since the last time Netscape was shut down. Returns 0 for success and NON Zero for failure
remember_history_file 4 Records information about current history file at exit so it can be checked at startup by check_history_file. No return value.
I encourage you to look over the source code and understand it. Alright enough about this. Lets get to it!
*************************** * Part 1: Protecting the "Clear History" button ***************************
OK, the mission for this part is to protect the "Clear History" button in the preferences. First, we'll need to find a suitable place to hook into the button. Then, call our dll function ask_for_passwd. Finally, we'll proceed with the code or not based on the results of the password compare.
Let's see what the behavior of the button is. Do this: Edit->Preferences Then go to the Navigator section. Press the button. You'll see a nice little messagebox pop up asking if we really mean it. Cool, this is our entry point.
Set a bpx on MessageBoxA. Press the button and SICE fires. Press F-12 to get back to where it was called from. From other reversing essays we know that every button/control in a window has an ID associated with it. When a button or menu item is pressed, a program typically switches/cases on the ID of the button pressed. If it finds a match for the ID's it knows about a routine is executed. Otherwise it keeps looking for a match 'till it runs out of known ID types. The logic looks something like this:
If ID pressed is equal to 1 -> do something for ID 1 else if ID pressed is equal to 2 -> do something for ID 2 .... and so on....
This means that when we return from the messagebox if we trace up a bit we should see a compare statement testing the ID that was pressed and some conditional jump statements. Here it is:
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:60261C35(C), :60261C3D(C) | :60261D23 81F90A040000 cmp ecx, 0000040A **** check to see if ID is 0x40A :60261D29 0F8592000000 jne 60261DC1 **** If Not jump around this and keep looking :60261D2F 85C0 test eax, eax **** Make sure eax is 0 :60261D31 0F858A000000 jne 60261DC1 **** if not jump around this and keep looking :60261D37 8D4DE8 lea ecx, dword ptr [ebp-18] **** otherwise execute this routine
* Reference To: nsdlg32.Ordinal:013B, Ord:013Bh | :60261D3A E8BD570000 Call 602674FC :60261D3F 8D4DDC lea ecx, dword ptr [ebp-24]
* Reference To: nsdlg32.Ordinal:013B, Ord:013Bh | :60261D42 E8B5570000 Call 602674FC
* Possible Reference to String Resource ID=00115: "This will clear the list of pages you have previously visite" | :60261D47 6A73 push 00000073
... A BUNCH OF CODE REMOVED ...
* Reference To: USER32.MessageBoxA, Ord:0195h | :60261D73 FF15E8A42660 Call dword ptr [6026A4E8] :60261D79 83F801 cmp eax, 00000001 ******** LAND HERE after MessageBoxA
OK cool, we've found it. Notice the module we're in is brpref32.dll. If you open this with your Resource Editor you'll notice that the clear history button is in dialog 103 and it's ID is 1034 or 0x40a in hex. This confirms the compare at 0x60261d23.
Before we continue, we need to add a way to change the password. I'm mentioning this here because I'm going to add a button right next to the clear history button for this purpose. The ID will be handled in this routine I've shown above. The main thing here is I'm going to add a resource to this dialog with the resource editor. Therefore, before we go editing and modifying the file brpref32.dll we need to add this button first since the resource editor will add this to the file and change the size of things around. So, open up your Resource editor and open brpref32.dll. Go to dialog 103, and edit it to add another button next to the clear history button. Name it "Change pass" and the resource editor should assign it an ID. It gave mine an ID of 1036 or 0x40c. Save the file and exit the resource editor. If you anaylze the file before and after with the PE editor of ProcDump32 you'd notice the resource editor added the dialog in the .rsrc section. See Part 3 for how to add code behind this button. The key point here is to edit the dll first with any resources you need to add. That way when you go to add code into the file it won't get overwritten or have offsets change on you.
Now we now know we need to add code starting at 0x60261d37 to jump to code in a free space of the dll. Lets find some space to add code. Open up ProcDump and use the PE Editor on brpref32.dll. Look at the sections of the file. Usually the last section of the file has some left over space for us to put some code of our own. Looking at this file we have the .reloc section which has a Raw Size of 0xC00 and a Virtual Size of 0xA94. Doing a simple subtraction we get 0xC00 - 0xA94 = 0x16C = 364 bytes free. That's more than enough for what we need.
OK now to add the code we need. Where to add it you ask? Lets see, The .reloc section has a Raw Offset of 0xF200 and a Raw Size of 0xA94. Therefore, a simple addition will get you the Relative Virtual Address (RVA) of where we can start to add our code. 0xF200 + 0xA94 = 0xFC94.
Alright, we need to call our ask_for_passwd function. We'll do this by Loading up the dll using LoadLibraryA, getting the address of the function using GetProcAddress, and then we'll call the function. No problem right? Looking at the Imports of bpref32.dll neither of these functions are imported from Kernel32.dll. Oh man, our first problem. Well, we need these functions, so maybe we can get their addresses another way. We can try to get the base address of Kernel32.dll by using GetModuleHandle, then get the functions address by using GetProcAddress. Hmmm... first of all this won't work because we don't have GetProcAddress imported and neither is GetModuleHandle. This is a real bummer. We can't get the addresses of any other functions from this dll. If GetModuleHandle was imported we might be able to get the base address of Kernel32.dll and scan it's exports in the PE header to find the address we need, but this isn't the case. OK time to use our brains.
*** Brainstorming *** OK I can't get the address of the functions I need. How am I going to do this then? hmm... maybe those functions exist in another dll/exe which is imported by brpref32.dll. Looking at the imports you see brpref32.dll imports functions from 2 Netscape specific dll's. Namely nsdlg32.dll and xppref32.dll. Looking at the imports of nsdlg32.dll I don't see the functions I need. Looking at the imports of xppref32.dll I see the functions I need. COOLIO ;). You'll see a few imports from xppref32.dll in brpref32.dll so lets pick 1. I choose ClearUserPref the first one on the list. Now, what if we could change this imported function somehow so we could pass another parameter to it. This parameter would indicate to the function if it should continue like normal or it should do something special (like get the address and call our function). This may not be the cleanest solution but it works and prevents us from having to do some complicated and bizzare things to try and find the addresses we need. ** END Brainstorming ***
So now we'll add some code in brpref32.dll to simply push a special parameter and call the function ClearUserPref in xppref32.dll. OK open up brpref32 in HVIEW and switch to decode mode. Use F-5 to go to offset F694. You'll notice that the global address for 0xf694 is 0x60271a94. Please note your addresses may not match mine exactly. Also note that you can switch between Global and Local addresses in HVIEW by pressing Alt-F1.
OK, now we need to make the program jump to our code at 0x60271a94 from 0x60261D37 as we saw above. So we'll add the following jump in place of the lea and call instructions as shown below:
:60261D23 81F90A040000 cmp ecx, 0000040A :60261D29 0F8592000000 jne 60261DC1 :60261D2F 85C0 test eax, eax :60261D31 0F858A000000 jne 60261DC1 :60261D37 E9580D0100**************jmp 60272A94 **** Jump to our code :60261D3C 90 nop **** NOP out the extra bytes of the call :60261D3D 90 nop **** that got overwritten :60261D3E 90 nop :60261D3F 8D4DDC lea ecx, dword ptr [ebp-24]
I've used the softice "a" (assemble) instruction to get the correct opcodes for the jmp. I've noted this by putting the stars (*) between the opcode and the mnemonic. I will continue with this notation throughout. It's not very easy to calculate these jump opcodes when the jump is far away. The "a" command in softice is best for this. Now looking at the code I've added at 60272a94:
:60272a94 60 pushad **** Save off all the registers :60272a95 6A01 push 1 **** Push a 1 to tell ClearUserPref this is a special call :60272a97 ff1524A62660 call dword ptr [6026a624] **** Call ClearUserPref :60272a9d 83C404 add esp,04 **** adjust stack :60272aa0 85C0 test eax,eax **** see if return was 0 = success :60272aa2 61 popad **** restore registers :60272aa3 0f8500f4feff************jne 60261ea9 **** if not 0 then jump back to end without clearing history :60272aa9 8d4de8 lea ecx,dword ptr [ebp-18] **** If ok restore the 2 instructions we :60272aac ff1518a52660 call dword ptr [6026a518] **** overwrote by jumping to our code here :60272ab2 e988f2feff**************jmp 60261d3f **** return to the function so the history gets cleared
It is worthwhile to note that calling a function is a simple case of calling the dword pointer in the FirstThunk array of the files Import Table. This pointer can be found in W32Dasm by searching through the disassembly for jmp/call dword ptr [xyz]. Then using xyz you can easily call that function from anywhere in your own code. See the call at 0x60272a97 for an example. You can see that I call the function I'm going to modify with an extra parameter of 1. This will be an indication to the function that it needs to do our special stuff instead of proceeding in it's normal fashion. You can see that if the return value is not 0 (password was incorrect) i jump back to the spot where the clear history finishes up, not executing any of the code in that routine, thus never actually clearing the history. Here's the code where I jump back to in that case.
* Reference To: nsdlg32.Ordinal:011B, Ord:011Bh | :60261EA4 E8CF550000 Call 60267478
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:60261C2A(U), :60261D1E(U), :60261DBC(U), :60261E9B(U) | :60261EA9 5F pop edi **** Jump back here to just finish the routine :60261EAA 5E pop esi **** so that it never actually clears the history :60261EAB 8BE5 mov esp, ebp :60261EAD 5D pop ebp :60261EAE C20C00 ret 000C
Otherwise we jump back to 0x60261d3f to continue like normal, allowing the history to clear cuz the password was correct.
OK now we need to look at xppref32.dll and modify it to check for this special parameter being pushed and call our funtion. Actually, we're going to check for 2 special parameters being pushed because we're going to use this same hook later on when we add the code to change the password. So, open up xppref32.dll in W32Dasm and find the exported function ClearUserPref. I've found it at 0x603f55be. Now we need to inject some code at the start of this function to check for our special parameter and either call our dll function and return or come back to the start and continue like normal. Let's find some space to add code to this dll. Open xppref32.dll in ProcDump's PE editor and once again look at the sections. Looks like the .rdata section has some space to add stuff. We see that this section has 484 bytes free. That's more than enough. We can add our code at 0x1ec1c. Refer to the calculations above on how to get these numbers. First we'll need to add a string reference to our custom dll in xppref32 so we can pass it to the LoadLibraryA function. I suppose we could do this via our resource editor but I just added it right before I added code at 0x1ec1c. Our custom dll is named "nspc.dll" so the ASCII codes for that are 6E 73 70 63 2E 64 6C 6C 00. I added these at RVA 0x1ec1c and it's 8 bytes long plus 1 for a null character so our real code will start at 0x1ec26 or global address 0x603ffc26. So now we jump to our real code at the start of the ClearUserPref function in xppref32.dll as follows:
Exported fn(): PREF_ClearUserPref - Ord:00FDh :603F55BE E963A60000**************jmp 603FFC26 **** jump to our new code :603F55C3 90 nop **** get rid of spare bytes :603F55C4 90 nop :603F55C5 56 push esi
And now lets look at the code I added at 0x603ffc26, I've used IDA to display this since I can't look at the .rdata section with W32Dasm.
:603FFC1C 6E 73 70 63 2E 64 6C 6C+aNspc_dll db 'nspc.dll',0 ; DATA XREF: .rdata:603FFC3Bo :603FFC25 00 align 2 :603FFC26 :603FFC26 loc_0_603FFC26: ; CODE XREF: PREF_ClearUserPrefj :603FFC26 8B 44 24 04 mov eax, [esp+4] **** get the 1st thing on the stack :603FFC2A 83 F8 01 cmp eax, 1 **** see if it's a 1 (special number) :603FFC2D 74 0C jz short loc_0_603FFC3B **** If so go call our function :603FFC2F 83 3D 80 2C 40 60 00 cmp dword_0_60402C80, 0 **** Otherwise restore the instruction we blew away :603FFC36 E9 8A 59 FF FF***********jmp loc_0_603F55C5 **** jump back to begining of function :603FFC3B ; --------------------------------------------------------------------------- :603FFC3B :603FFC3B loc_0_603FFC3B: ; CODE XREF: .rdata:603FFC2Dj :603FFC3B 68 1C FC 3F 60 push offset aNspc_dll ; "nspc.dll" **** Push "nspc.dll" :603FFC40 FF 15 84 67 40 60 call ds:LoadLibraryA ; LoadLibraryA: **** call LoadLibraryA :603FFC46 8B F0 mov esi, eax **** save handle in esi for later :603FFC48 8B 5C 24 08 mov ebx, [esp+8] **** get the 2nd thing passed on stack :603FFC4C 83 FB 01 cmp ebx, 1 **** if it's also a 1 :603FFC4F 74 07 jz short loc_0_603FFC58 **** This is a request to change pass :603FFC51 6A 01 push 1 **** otherwise ordinal = 1 = ask_for_pass :603FFC53 E9 02 00 00 00 jmp loc_0_603FFC5A **** jump to call getprocaddress :603FFC58 ; --------------------------------------------------------------------------- :603FFC58 :603FFC58 loc_0_603FFC58: ; CODE XREF: .rdata:603FFC4Fj :603FFC58 6A 02 push 2 **** ordinal = 2 = change_password :603FFC5A :603FFC5A loc_0_603FFC5A: ; CODE XREF: .rdata:603FFC53j :603FFC5A 50 push eax **** Handle to nspc.dll :603FFC5B FF 15 88 67 40 60 call ds:GetProcAddress ; GetProcAddress: **** Get the functions address :603FFC61 FF D0 call eax **** Call our function in the dll :603FFC63 8B D8 mov ebx, eax **** save off the return value :603FFC65 56 push esi **** handle to nspc.dll :603FFC66 FF 15 8C 67 40 60 call ds:FreeLibrary ; FreeLibrary: **** Free the Library :603FFC6C 8B C3 mov eax, ebx **** Restore return value to eax :603FFC6E C3 retn **** return to brpref32.dll
That's all there is to part 1. You should now have a password dialog pop up each time you press the Clear History button.
*************************** * Part 2: Protecting manual deletions when you view the history ***************************
In Netscape you can view the history by pressing CTRL-H or by selecting the menu item Communicator->Tools->History.
When in this window you can select any number of items of the history and manualy delete them using the delete key on your keyboard. This is what we need to protect against. OK lets look at the characteristics of the program when we hit CTRL-H. You'll notice a window pops up. Well Windows are usually created using the API function CreateWindowExA. Put a breakpoint on this API and try again. SICE will fire. We've found the function Netscape uses to create the History window. If you do a "d esp->c" in SICE when it breaks you'll notice that the window's title is History. This further confirms we're in the right place. If we keep going you'll notice the program breaks on CreateWindowExA several times (once for each sub window). Following the code up you'll notice the CreateWindowExA function is being called from a module called MFC42. Yuck! This is Microsoft Foundation Classes. OK so we really don't want to patch the MFC dll rather a netscape exe or dll. Following the code up some more, you'll eventually land inside netscape.exe at 0x006f0b2c. Upon Disassembly in IDA you'll notice this is a call to a MFC function called Create in the Class CWnd::. OK on my first approach, I tryed to basically change the window style of the inner panel to WS_DISABLE. That way, the items of history can't be selected or deleted. This proved to be easy to change dynamically when I was debugging, but wasn't so obvious back at the call to Create in netscape.exe. Seemed like the styles used in the call to Create were only for the outermost window. Then somehow the MFC routine knows to call CreateWindowExA several times based on someting?? A window structure maybe? I'm not sure. I didn't want to change the style of the outermost window to WS_DISABLE though because then the window wouldn't take any input from the user. Not even allowing it to be closed. Another approach is needed because I'm starting to get lost in MFC documentation and code.
Approach 2: Why don't I trace the code up the call chain until I get to the highest level I can find, where I can either call the function or not based on the password. This way if the password is entered correctly, the history window will show up, and if the password is incorrect the window will never appear. I traced through the code about 3 or 4 call levels up and found the following code:
.text:006E7094 C3 retn **** End of someother Subroutine .text:006E7095 6A 00 push 0 **** Push a parameter .text:006E7097 E8 A6 AB 00 00 call sub_0_6F1C42 **** Call our display the window routine .text:006E709C 83 C4 04 add esp, 4 **** Adjust stack .text:006E709F C3 retn **** and return...
So, this seems to be a nice place to insert our own code. But first lets find a place to put it. Open up netscape.exe in ProcDump32 and look at the sections. You'll notice there seems to be some space in the .text section. 0x3d8c00 - 0x3d8a1a = 0x1e6 = 486 bytes free. OK that's plenty. Now where to put it: 0x600 + 0x3d8a1a = 0x3d901a RVA to place the code. OK once again we'll need a string to our dll so at 0x3d901a lets add nspc.dll in ASCII (6E 73 70 63 2E 64 6C 6C 00). This puts us at 0x3d9023 but since I like to start on even boundaries lets start our code at 0x3d9024 which is 0x007d9a24 as a global address.
Let's now jump to our new code from 0x006e7097 like so:
.text:006E7094 C3 retn .text:006E7095 E9 8A 29 0F 00******jmp near ptr unk_0_7D9A24 **** Jump to our new code .text:006E709A 90 nop **** Nop out the 2 extra bytes .text:006E709B 90 nop .text:006E709C 83 C4 04 add esp, 4 .text:006E709F C3 retn
And Now to look at the code added:
.text:007D9A1A 6E 73 70 63 2E 64 6C 6C 00 anspc db 'nspc.dll',0 ; DATA XREF: .text:007D9A25o .text:007D9A23 00 db 0 ; .text:007D9A24 ; --------------------------------------------------------------------------- .text:007D9A24 .text:007D9A24 loc_0_7D9A24: ; CODE XREF: .text:006E7095j .text:007D9A24 60 pusha **** Save off context .text:007D9A25 68 1A 9A 7D 00 push offset anspc ; "nspc.dll" **** our custom dll .text:007D9A2A FF 15 44 72 90 00 call ds:LoadLibraryA ; LoadLibraryA: **** load the library .text:007D9A30 8B F0 mov esi, eax **** Save handle for freeing later .text:007D9A32 6A 01 push 1 **** Ordinal 1 = ask_for_passwd .text:007D9A34 50 push eax **** push module handle .text:007D9A35 FF 15 48 72 90 00 call ds:GetProcAddress ; GetProcAddress: **** Get the address of function .text:007D9A3B FF D0 call eax **** Call the dll function .text:007D9A3D 8B D8 mov ebx, eax **** save the result of call .text:007D9A3F 56 push esi **** Push module handle .text:007D9A40 FF 15 40 72 90 00 call ds:FreeLibrary ; FreeLibrary: **** Free the dll library .text:007D9A46 8B C3 mov eax, ebx **** restore return value in eax .text:007D9A48 85 C0 test eax, eax **** see if result is 0 = success .text:007D9A4A 61 popa **** restore context .text:007D9A4B 0F 85 4E D6 F0 FF******jnz locret_0_6E709F **** jmp back to 0x006e709f on a **** failure so just return with **** no stack adjustment .text:007D9A51 6A 00 push 0 **** otherwise, restore instructions **** we overworte by jumping here .text:007D9A53 E8 EA 81 F1 FF*********call loc_0_6F1C42 .text:007D9A58 E9 3F D6 F0 FF*********jmp loc_0_6E709C **** and return to 0x006e709c **** as if nothing ever happened
OK so once again you can see how I've jumped to some new code called the dll to ask for the password and then returned back to the main program in 2 ways. 1 way for success, which carries on like normal, or 2, a way in which nothing happens because the password was entered wrong.
That's it to part 2. You should now have a password dialog box pop up anytime you do a CTRL-H or select History from the pull-down menu items in Netscape.
*************************** * Part 3: Adding functionality to the change password button added in part 1 ***************************
OK, as you saw we added a change password button right next to the clear history button in the dialog in brpref32.dll in part 1 of this essay. This was done with our resource editor with a simple copy and paste of another button. We also make note that this new button has an ID of 1036 or 0x40c. As we saw in part 1, we found the routine where Netscape checks the ID and executes a routine based on which control was pressed. Well, since this new button is part of the same dialog it will also use this same routine to execute something when it it pressed. This ID that the resource editor assigned to our button is unique. Therefore, Netscape currently doesn't check for it. We'll need to add code to do that and to execute our dll function if our new button was pressed.
Lets look at the code to see how we can insert some code to handle this button being pressed.
From part 1 of this essay we saw brpref32.dll checking the clear history button like follows:
.text:60261D23 cmp ecx, 40Ah **** was ID 40a pressed? .text:60261D29 jnz loc_0_60261DC1 **** If Not jump to next possible ID
...
.text:60261DC1 cmp ecx, 40Bh **** was ID 40b pressed? .text:60261DC7 jnz loc_0_60261E9D **** If Not jump to next possible ID
...
.text:60261E9D push eax **** No more ID's to process so clean up and exit .text:60261E9E push dword ptr [ebp+0Ch] .text:60261EA1 push ecx .text:60261EA2 mov ecx, esi .text:60261EA4 call j_nsdlg32_283 .text:60261EA9 .text:60261EA9 loc_0_60261EA9: ; CODE XREF: .text:60261C2Aj .text:60261EA9 ; .text:60261D1Ej ... .text:60261EA9 pop edi .text:60261EAA pop esi .text:60261EAB mov esp, ebp .text:60261EAD pop ebp .text:60261EAE retn 0Ch
This seems to make sense since the ID for our new button is 0x40c and the last thing brpref32 checks is 0x40b. I bet you're already thinking what we need to do here. At 0x60261e9d, we're going to jump to some new code of ours which is going to check if ID 0x40c was pressed and if so call our dll function change_password. Otherwise it's going to come back here to clean up and exit gracefully.
We can add our code right below the code we added in brpref32.dll in part 1. We ended code there at 0x60272ab6 so we can start our code for this part at 0x60272ab7.
Let's jump to our new code first:
.text:60261E9D E9 15 0C 01 00****jmp near ptr 60272AB7h **** Jump to our new code .text:60261EA2 8B CE mov ecx, esi .text:60261EA4 E8 CF 55 00 00 call j_nsdlg32_283
And here's our new code:
:60272ab7 81f90c040000 cmp ecx,40c **** Was ID 40c pressed? :60272abd 740a jz 60272ac9 **** If so go handle it :60272abf 50 push eax **** otherwise restore the instructions we blew away :60272ac0 ff750c push [ebp+c] **** from jumping here :60272ac3 51 push ecx :60272ac4 e9d9f3feff************jmp 60261ea2 **** and jump back to where we were :60272ac9 60 pushad **** save context :60272aca 6a01 push 1 **** push special parameter to say this is a change password :60272acc 6a01 push 1 **** Push a 1 to tell ClearUserPref this is a special call :60272ace ff1524a62660 call PREF_ClearUserPref **** call the function :60272ad4 83c408 add esp,8 **** adjust the stack by 8 cuz we pushed 2 things :60272ad7 e9cdf3feff************jmp 60261ea9 **** jump back to program to return correctly
That's it to part 3. You should now be able to press the Change Pass button and the dialog I've created in the nspc.dll will pop up asking you to change the password. A messagebox will pop up letting you know if the password was changed or not. This is based on entering the correct old password.
*************************** * Part 4: Protecting the number of days after which pages in history expire ***************************
Well, after some stuggle this one's going to be an easy one. I'd really like to investigate this one further, but haven't had too much time. If you've noticed, there's a field in the preferences to select the number of days after which the pages in the history expire. We need to protect against someones ability to change this and effectively tell Netscape to blow away where they'd been shortly thereafter. So, this is what I did: Fist, use your windows spy tool to get some information on the dialog box at the right of the preferences window. I'm using Spy32 so there's this neat little selector thing to drag over the window in question. When doing that be careful to get the correct window. you don't want the small dialog with the edit box in it but rather the rightmost part of the window. Try it and you'll see what I mean. If you go to the Class tab in Spy32 you'll notice the address of the window procedure for this window is: 0x77e847a0. This may be different for you but it's what I see. That seems like an odd address. Looks like it's most likley in kernel memory somewhere. If you open up SICE with a Ctrl-D, and do a u 0x77e847a0 you might not see and code but the function name is USER32!DefDlgProcA. OK let's set a breakpoint on that function and click on the edit box. You kind of have to set the mouse in the right position beforehand otherwise SICE will fire constantly. OK click the edit box without moving the mouse. SICE should fire. Now hit F-12 a bunch of times until you come out of the kernel/user woods. You should be in nsdlg32.dll here:
.text:60291EFA push [ebp+arg_8] .text:60291EFD push [ebp+arg_4] .text:60291F00 push [ebp+arg_0] .text:60291F03 push dword ptr [esi+0Ch] .text:60291F06 push dword ptr [esi+10h] .text:60291F09 call ds:CallWindowProcA ; CallWindowProcA: .text:60291F0F .text:60291F0F loc_0_60291F0F: ; CODE XREF: nsdlg32_284+2Fj .text:60291F0F pop esi ******************** RETURN HERE .text:60291F10 pop ebp .text:60291F11 retn 0Ch
As you can see, Netscape calls CallWindowProcA to process these messages. OK, so now lets look at the WM_COMMAND messages going by as this is what Netscape will use to notify the parent window when something happens to a control in this dialog. We know that this is the function prototype for CallWindowProcA:
LRESULT CallWindowProc(
WNDPROC lpPrevWndFunc, // pointer to previous procedure HWND hWnd, // handle to window UINT Msg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );
So the message is the 3rd parameter passed to the function. OK, we also know that WM_COMMAND is defined as
#define WM_COMMAND 0x0111
in winuser.h. so we'll be looking for that along with the params of the WM_COMMAND message which looks like this:
WM_COMMAND wNotifyCode = HIWORD(wParam); // notification code wID = LOWORD(wParam); // item, control, or accelerator identifier hwndCtl = (HWND) lParam; // handle of control
OK, lets set a breakpoint in SICE and have it tell us what was pushed as the message to the function call. do this in SICE: bpx 60291F09 do "d esp->8" That will display the 3rd parameter passed to the function in the data area of the SICE window when it breaks. Now once again click in the edit box and you'll see many breaks in SICE. Just keep F-5'ing past the ones that aren't 0x111. Once you get to 1 do a d esp->c to see the WParam argument of the call. This should be 0x010003f1. As we saw from the WM_COMMAND message the lower part is the control ID and the upper part is the notification code. Opening up your resource editor and looking at Dialog 103 in brpref32.dll you'll notice the ID for the edit box is indeed 0x3f1. OK that jives. Now for the notification code, well I just played around a bit. Looking at the Notification codes when doing some things with the edit box should give us some clues. Clicking in the editbox we get a notification code of 0100. Clicking in the other editbox in the same dialog we see a notification code of 0200. When adding or deleting text from the edit box we see notification codes 0300 and 0400. OK I think now after some playing we can now draw up a table of Notification codes for the edit box:
Notification code : Meaning ============================ 0100 Activate editbox 0200 Deactivate editbox 0300/0400 Text addition/removal (both notification codes occur)
So we now can inject code at the call to CallWindowProcA to check to see if we are activating our editbox by checking to see if the message is 0x111 and the WParam is 0x010003f1. If it is we'll call our dll code to ask for a password and restore the CallWindowProcA call and resume execution.
The big problem is that I can't seem to add code to this nsdlg32.dll. Once again I am faced with the problem of not having the windows API functions I need to get the address of the my custom dll functions. There also doesn't seem to be any reasonable imports to modify like I did in part 1. I could mess around with modifying user32.dll to modify CallWindowProcA in much the same way as in part 1. That could be dangerous and not very portable. So, when in need of reversing help I talked with one of the greatest Fravias I know, Neural Noise. He suggested to insert a memory scanning routine that will backwards scan memory looking for a PE signature, then by starting in the right place we'd know this is Kernel32.dll and then by scanning it's export table we could find the address to whatever function we need. He was even nice enough to write some scanning code to do it. Thank you very much nu :) Anyways this scanning code seemed to work until I moved it to NT. This is because we can't freely access kernel memory from a Ring-3 application. We'd have to be in ring 0 for that. So the scanning code doesn't seem to work for NT. It's not easy to just jump into ring 0 in NT as far as I know. It's not like in 9x when you can just hack ring 0 from DPMI. So I'm stuck once again. In this never-ending loop. I've not gone any further with this scanning code, but I've included it and leave it up to you to inject it if you so wish.
At this point I was starting to loose the ability to make a clean solution for both OS's following this track. Anyways, what I've decided to do, is to simply make the edit box read-only. That way no one will be able to edit it. Yeah, I know that's kind of the cheap way out but it works.
For this task, open up your resource editor and open brpref32.dll. Go to dialog 103 and edit the resource. Select the edit box associated with the Expiration of the history and check off Read-Only as a Style. Save and quit the editor. Now when you open Netscape preferences you should have a greyed-out box for that field.
*************************** * Part 5: Protecting against history file modification on the disk. ***************************
This is going to be an interesting section imho. This involves a little research and digging into our target to make sure we do the right things, as you'll see in a bit. First, I'd like to explain the theory behind this section.
Adding passwords is great but if a smart enough person realizes this, they may be able to delete or modify the actual file on the harddrive to cover their tracks. This presents a problem which can be solved fairly easily. I think it's best to explain this in reverse order since that's where things start. Netscape keeps a history file per profile in the Users\<profile_name> directory called netscape.hst. So the idea here is, every time Netscape is closed, we'll store values in the registry that reflect the history files current state. Namely these things are last time the file was written, and the file size. I think it would also be easy to modify the dll to include some other file attributes like creation time etc... but this example will be enough to provide some basic protection. Then, when Netscape is opened again, the history file is looked at again and compared to the values in the registry. If these values differ then a password will be required to run Netscape again. Otherwise Netscape will start up normally. We must keep in mind we'll want to store the values to the registry as LATE as possible in the closing of Netscape to ensure Netscape itself has finished writing the file. And we'll compare the file and registry values as EARLY as possible in the opening of Netscape to ensure it hasn't opened the file yet. That way we can be sure that the numbers will match when the file hasn't been touched between run's of Netscape. There is no need to protect against modification of this file while Netscape is open. I determined this by studying how Netscape operates. If you pay attention to the history file you'll notice it gets updated once when Netscape closes. If you edit the file while Netscape is open Netscape simply overwrites the changes you've made. So therefore, there's no need to protect that.
There's one more thing we need to know. Since history is on a per profile basis how do we know what profile we're currently using? Well, we're not the only ones that need to know this so there must be a function to retrieve it for us somewhere. If you remeber from part 1 we used a dll called xppref32.dll which had some fuctions in it to deal with preferences. Lets look there. Browsing through the exports you see stuff about ProfileManager etc... Looks promising. Then I stumbled on this function: PREF_GetCurrentProfile. Looking at it we see this:
.text:603F6D42 public PREF_GetCurrentProfile .text:603F6D42 PREF_GetCurrentProfile proc near ; CODE XREF: ProfileNameChanged+14p .text:603F6D42 ; ProfileTempChanged+Ap ... .text:603F6D42 mov eax, dword_0_60403114 .text:603F6D47 retn .text:603F6D47 PREF_GetCurrentProfile endp
Seems real simple. This routine seems to move a global variable into eax and return. It couldn't get much simpler.
OK now to find some suitable places to inject our dll code.
I'll first start in reverse order, like I said. Since it makes more sense to first exit the program storing the values, then start the program so the values are in place for comparison.
We need to find a spot to inject as close to the end of the program as possible. Lets set breakpoints on typical functions like TerminateProcess, TerminateThread, ExitProcess, etc... Close Netscape and DAMN SICE doesn't fire. So the program doesn't use one of these functions to exit. hmmm... we know Netscape closes the history file fairly late maybe we can trigger off of that and step a little to find a suitable place. Lets set breakpoints on CloseHandle, WriteFile, lwrite. hmm... Still no breaks. That's strange we know it writes the file on exit. Maybe the file is mapped into memory and unmapped on exit. Set a breakpoint on UnmapViewOfFile. Close Netscape, and BOOM, SICE fires. Cool. Now if you F-12 several times you'll be brought back to the netscape module via a module called fullsoft. You'll land here:
.text:0073A7C2 loc_0_73A7C2: ; CODE XREF: ExitFunction+7j .text:0073A7C2 A1 C4 50 8C 00 mov eax, dword_0_8C50C4 *******************LAND HERE .text:0073A7C7 85 C0 test eax, eax .text:0073A7C9 74 07 jz short locret_0_73A7D2 .text:0073A7CB 50 push eax .text:0073A7CC FF 15 40 72 90 00 call ds:FreeLibrary ; FreeLibrary: .text:0073A7D2 .text:0073A7D2 locret_0_73A7D2: ; CODE XREF: ExitFunction+12j .text:0073A7D2 C3 retn
If you step through this to the ret you'll be dropped back into MSVCRT and if you look down you'll see a call to ExitProcess. I'm not sure why the breakpoint didn't fire before but oh well. This seems to be the place we're interested in. So Lets jump to our new code at 0x0073a7c2. We can add our code right below what we added in part 2. We ended there at 0x007d9a5c so we could add our code at 0x007d9a5d. I actually added a huge string I thought I needed there but ended up not needing so I started my code at 0x007d9a85 because I'm too lazy to re-do it. Let's look at the jump to our new code first:
.text:0073A7C2 E9 BE F2 09 00********jmp loc_0_7D9A85 **** Jump to our new code .text:0073A7C7 .text:0073A7C7 loc_0_73A7C7: ; CODE XREF: .text:007D9AB7j .text:0073A7C7 85 C0 test eax, eax .text:0073A7C9 74 07 jz short locret_0_73A7D2
and now for our new code:
.text:007D9A85 loc_0_7D9A85: ; CODE XREF: .text:0073A7C2j .text:007D9A85 60 pusha **** Save context .text:007D9A86 68 1A 9A 7D 00 push offset 7D9A1A **** "nspc.dll" as in part 2 .text:007D9A8B FF 15 44 72 90 00 call ds:LoadLibraryA ; LoadLibraryA: **** load the dll .text:007D9A91 8B F0 mov esi, eax **** save module handle for freeing .text:007D9A93 6A 04 push 4 **** ordinal 4 = remember_history_file .text:007D9A95 50 push eax **** module handle .text:007D9A96 FF 15 48 72 90 00 call ds:GetProcAddress ; GetProcAddress: **** get address of dll function .text:007D9A9C 8B D8 mov ebx, eax **** save off for later .text:007D9A9E FF 15 DC 9C 90 00 call ds:PREF_GetCurrentProfile_0 **** get the current profile .text:007D9AA4 50 push eax **** Push it as parameter to function .text:007D9AA5 FF D3 call ebx **** call dll function .text:007D9AA7 83 C4 04 add esp, 4 **** adjust stack seeing function didn't .text:007D9AAA 56 push esi **** push module handle .text:007D9AAB FF 15 40 72 90 00 call ds:FreeLibrary ; FreeLibrary: **** Free the dll .text:007D9AB1 61 popa **** restore context .text:007D9AB2 A1 88 37 90 00 mov eax, dword_0_903788 **** restore intruction we blew away .text:007D9AB7 E9 0B 0D F6 FF*****jmp loc_0_73A7C7 **** go back to where we came from
So, as you can see here, we've called our dll function remember_history_file with a parameter that we got by calling a function provided by a Netscape dll. Very cool. One thing to note is that function returns the profile name looking like this p.?`profilename so my dll function takes this into account and strips off the first 4 characters of the argument passed.
Now to check the stuff on startup...
I started by loading netscape.exe into the Softice symbol loader and stepping from the entry point. I tryed calling the function to get the profile name but it returned a NULL. Must be because that stuff hasn't been set up yet as the program hasn't even run yet.So we must find another spot. If you keep stepping you'll see the program get some startup info then get a module handle the calls a function which starts Netscape. I knew Netscape accessed the profile somehow and frankly I don't remember how I found this but Netscape calls a function called CProfileManager::Authenticate on startup. Here's the start of that function:
.text:603F020D public ?Authenticate@CProfileManager@@SAHPAVCProfile@@@Z .text:603F020D ?Authenticate@CProfileManager@@SAHPAVCProfile@@@Z proc near .text:603F020D .text:603F020D var_4C8 = byte ptr -4C8h .text:603F020D var_C8 = byte ptr -0C8h .text:603F020D var_64 = byte ptr -64h .text:603F020D arg_0 = dword ptr 8 .text:603F020D .text:603F020D push ebp .text:603F020E mov eax, [esp+arg_0] .text:603F0212 mov ebp, esp .text:603F0214 sub esp, 4C8h
So you'll need to set a breakpoint at 0x603f020d. But we need an entryway into xppref32 to be able to set it. Well, I used the entrypoint I created in part 1 of this essay. If you press the Clear History button and step through the code I added it will bring you into xppref32 where we can set a break on this address. Start up Netscape and you'll break on this function. Then Hit F-12. You'll be returned here:
.text:006FDC25 call xppref32!?Authenticate@CProfileManager@@SAHPAVCProfile@@@Z .text:006FDC2A 83 C4 04 add esp, 4 *** Land HERE .text:006FDC2D 85 C0 test eax, eax .text:006FDC2F 74 35 jz short loc_0_6FDC66 .text:006FDC31 C6 45 FC 03 mov byte ptr [ebp-4], 3 .text:006FDC35 E8 36 1B 00 00 call near ptr unk_0_6FF770
So looks like we can intercept this call at 0x006fdc25 and call our own code. Surely by now that global variable has been initialized.
Now lets look at the jump to our new code:
.text:006FDC25 E9 92 BE 0D 00****jmp near ptr loc_0_7D9ABC **** jump to our new code .text:006FDC2A 83 C4 04 add esp, 4 .text:006FDC2D 85 C0 test eax, eax .text:006FDC2F 74 35 jz short loc_0_6FDC66 .text:006FDC31 C6 45 FC 03 mov byte ptr [ebp-4], 3
Then, to look at our code added right below the stuff we added above:
.text:007D9ABC loc_0_7D9ABC: ; CODE XREF: .text:006FDC25j .text:007D9ABC 60 pusha **** Save context .text:007D9ABD 68 1A 9A 7D 00 push offset unk_0_7D9A1A **** "nspc.dll" .text:007D9AC2 FF 15 44 72 90 00 call ds:LoadLibraryA ; LoadLibraryA: **** Load the dll .text:007D9AC8 8B F0 mov esi, eax **** save handle off for freeing .text:007D9ACA 6A 03 push 3 **** ordinal 3 = check_history_file .text:007D9ACC 50 push eax **** module handle .text:007D9ACD FF 15 48 72 90 00 call ds:GetProcAddress ; GetProcAddress: **** get dll function address .text:007D9AD3 8B D8 mov ebx, eax **** save address for later .text:007D9AD5 FF 15 DC 9C 90 00 call ds:PREF_GetCurrentProfile_0 **** get the current profile .text:007D9ADB 50 push eax **** push as parameter to dll function .text:007D9ADC FF D3 call ebx **** call dll function .text:007D9ADE 83 C4 04 add esp, 4 **** adjust stack since function didn't .text:007D9AE1 8B D8 mov ebx, eax **** save result of call .text:007D9AE3 56 push esi **** module handle .text:007D9AE4 FF 15 40 72 90 00 call ds:FreeLibrary ; FreeLibrary: **** free the dll .text:007D9AEA 85 DB test ebx, ebx **** test result of call .text:007D9AEC 74 06 jz short loc_0_7D9AF4 **** if OK go on .text:007D9AEE FF 15 A4 82 90 00 call ds:_exit **** otherwise exit process .text:007D9AF4 .text:007D9AF4 loc_0_7D9AF4: ; CODE XREF: .text:007D9AECj .text:007D9AF4 61 popa **** restore context .text:007D9AF5 E8 BA 1E F5 FF**********call near ptr unk_0_72B9B4 **** restore instruction we blew away .text:007D9AFA E9 2B 41 F2 FF**********jmp loc_0_6FDC2A **** jump back to where we came from
You'll see that this works rather well. I studied other imports into netscape.exe to find this exit function being imported from MSVCRT.dll and it does the job of exiting the application when the wrong password is entered.
That's it for this section. You should now have the password dialog pop up if you modify the history file between runs of Netscape. One may ask what if someone deletes the file? Well, yes you won't have a record of where that person went, but at least they'll have to come see you about the password. At which time, the investigation can start.
*************************** * Final Thoughts ***************************
Wow, I'm finally done! I know I've learned a ton of stuff in the writing of this essay. I hope you have too. Hopefully you sayed with this essay I know it's quite long. Anyways I hoped I've shown some alternate ways of thinking and dealing with problems when they arise. Just don't give up, and think of it from another angle when the way you'd normally do it isn't possible. Anyways, I'd like to send some shouts out to all the people who helped me with this essay when I needed to ask a question or two. They are, in no particular order, Volatility, alpine_, X-Calibre, douby, VisionZ-, and the leetest Fravia I know, Neural Noise. Without the help of these guys at key times, This essay wouldn't exist. Lastly shouts out to all the members of The Immortal Descendants.
I've included the source code to the custom dll I wrote and also a copy of a SmartDownload file to get the version of Netscape I did this essay on from Netscape's ftp site. I've also included a compiled version of the custom dll too. Also attached is the scanning code provided by Neural Noise that I described in part 4.
To contact me on this essay with questions or problems you can do so at amante_4(at)yahoo(dot)com or I'm often on IRC at #ImmortalDescendants on EFNet.
I'm off,
Amante4