께께께께께께께께께께께께께께께께께께께께께께께께께께께
    께    ____                     __       __           께賞
    께   /  _/_ _  __ _  ___  ____/ /____ _/ /           께 幡
    께  _/ //  ' \/  ' \/ _ \/ __/ __/ _ `/ /            께  
    께 /___/_/_/_/_/_/_/\___/_/  \__/\_,_/_/             께  
    께   ____                          __          __    께  
    께  / __ \___ ___ _______ ___  ___/ /__ ____  / /____께  
    께 / /_/ / -_|_-</ __/ -_) _ \/ _  / _ `/ _ \/ __(_-<께  
    께/_____/\__/___/\__/\__/_//_/\_,_/\_,_/_//_/\__/___/께  
    께                                                   께  
    께      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