Verify your MSS box is booted with the "Windows Server 2003 for Small Business Server [Debug COM1]" menu option we added into the boot.ini file (above).
Now start one of the
HP Easter Eggs that does funky things to the LEDs. This will provide visual response as to whether a hardware command actually did something (like turn on or off an LED). I set my Easter Egg configuration to cycle all the hard drive LEDs with different colors. This will allow me to see how LEDs are turned on/off and what bit patterns causes the LED to turn red, blue or purple.
With both your desktop and your MSS running, launch WinDbg from your desktop computer. Make sure your WinDbg is configured to connect via the attached serial cable (via the "File-->Kernel Debug..." configuration screen). When ready, hit "Ctrl-break" (from within WinDbg) to break into your MSS operating system. If you have a VGA connection, verify that your MSS box is not responsive to anything (not even the clock or mouse should move). The Easter Egg LEDs (that you set up in the previous paragraph) should also be frozen. Although your box appears hung, it's not. All you've done is halt the computer such that you control each and every instruction that is run. Cool!
On your desktop, you should see something like this from your WinDbg session:
Code:
Microsoft (R) Windows Debugger Version 6.8.0004.0 X86
Copyright (c) Microsoft Corporation. All rights reserved.
Opened \\.\COM4
Waiting to reconnect...
Connected to Windows Server 2003 3790 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path. *
* Use .symfix to have the debugger choose a symbol path. *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is:
*********************************************************************
* Symbols can not be loaded because symbol path is not initialized. *
* *
* The Symbol Path can be set by: *
* using the _NT_SYMBOL_PATH environment variable. *
* using the -y <symbol_path> argument when starting the debugger. *
* using .sympath and .sympath+ *
*********************************************************************
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ntkrnlpa.exe -
Windows Server 2003 Kernel Version 3790 (Service Pack 2) UP Free x86 compatible
Product: Server, suite: SmallBusiness TerminalServer SmallBusinessRestricted SingleUserTS <8000>
Built by: 3790.srv03_sp2_gdr.070304-2240
Kernel base = 0x80800000 PsLoadedModuleList = 0x8089ffa8
Debug session time: Fri May 30 17:04:13.562 2008 (GMT-5)
System Uptime: 0 days 0:01:13.132
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
* *
* You are seeing this message because you pressed either *
* CTRL+C (if you run kd.exe) or, *
* CTRL+BREAK (if you run WinDBG), *
* on your debugger machine's keyboard. *
* *
* THIS IS NOT A BUG OR A SYSTEM CRASH *
* *
* If you did not intend to break into the debugger, press the "g" key, then *
* press the "Enter" key now. This message might immediately reappear. If it *
* does, press "g" and "Enter" again. *
* *
*******************************************************************************
nt!DbgBreakPointWithStatus+0x4:
8086cf14 cc int 3
kd>
Alright...we have halted Windows Server 2003 at the kernel level. Our MSS box is a sitting brick until we resume. The string "kd>" is our console prompt. Anything after this string is a command that I typed.
So let's load our debug symbols via the ".symfix" and ".reload" command.
Code:
kd> .symfix
No downstream store given, using C:\Program Files\Debugging Tools for Windows\sym
kd> .reload
Connected to Windows Server 2003 3790 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
................................................................................................
Loading User Symbols
Loading unloaded module list
.......
Let's see what modules (drivers) are loaded on our MSS:
Code:
kd> lm
start end module name
80800000 80a4c000 nt (pdb symbols) C:\Program Files\Debugging Tools for Windows\sym\ntkrnlpa.pdb\BB346E8288E5412A8AF4763DF1E71B7C1\ntkrnlpa.pdb
80a4c000 80a78000 hal (deferred)
b947e000 b94a9000 RDPWD (deferred)
b9661000 b966c000 TDTCP (deferred)
b9bd9000 b9c36000 srv (deferred)
b9c86000 b9cd6000 HTTP (deferred)
b9f76000 b9f87000 dump_SiSRaid2 (deferred)
b9fef000 b9ff9000 Dxapi (deferred)
ba02f000 ba039000 dump_diskdump (deferred)
ba09f000 ba0b0000 Fips (deferred)
ba0b0000 ba126000 mrxsmb (deferred)
ba126000 ba156000 rdbss (deferred)
.. (lines omitted)...
baf40000 baf4f000 termdd (deferred)
baf50000 baf59000 raspti (deferred)
baf70000 baf7af80 WNAS (deferred)<<<<<<<<<<<<<<<<<<< Cool, note to self, the driver is loaded at address 'baf70000'
baf80000 baf8b000 ptilink (deferred)
baf90000 baf9b000 TDI (deferred)
bf800000 bf9d0000 win32k (deferred)
.. (lines omitted)...
f7587000 f7590000 watchdog (deferred)
f7597000 f75a1000 mouclass (deferred)
f75a7000 f75b2000 kbdclass (deferred)
f75b7000 f75c0000 ndistapi (deferred)
f75c7000 f75d6000 raspppoe (deferred)
f76e1000 f7707000 KSecDD (deferred)
f7707000 f770f000 kdcom (deferred)
f770f000 f7717000 BOOTVID (deferred)
f7717000 f771e000 pciide (deferred)
f771f000 f7726000 dmload (deferred)
f77df000 f77e3400 usbohci (deferred)
f77e7000 f77edb80 usbehci (deferred)
f77ef000 f77f7000 audstub (deferred)
f7839000 f7858000 Mup (deferred)
f7858000 f7897000 NDIS (deferred)
f79eb000 f79ec280 swenum (deferred)
f79ef000 f79f0580 USBD (deferred)
f7a74000 f7a75000 cvintdrv (deferred)
f7b4a000 f7bdf000 Ntfs (deferred)
Let's get some more info on WNAS
The "lm" command displays the specified loaded modules. The output includes the status and the path of the module.
The "v" parameter causes the display to be verbose.
The "m" parameter (followed by a pattern) specifies a pattern that the module name must match.
Code:
kd> lm vm WNAS
start end module name
baf70000 baf7af80 WNAS (deferred)
Image path: \SystemRoot\system32\DRIVERS\WNAS.sys
Image name: WNAS.sys
Timestamp: Wed Apr 18 01:20:12 2007 (4625B89C)
CheckSum: 0000B659
ImageSize: 0000AF80
Translations: 0000.04b0 0000.04e0 0409.04b0 0409.04e0
Hmmmm...nothing too interesting here except that WNAS is loaded at address 'baf70000'. Let's try to load the symbols for WNAS and see if they are available on some Microsoft Symbol Server.
Code:
kd> .reload /f WNAS.sys
*** ERROR: Module load completed but symbols could not be loaded for WNAS.sys
kd> lm vm WNAS
start end module name
baf70000 baf7af80 WNAS (no symbols)
Loaded symbol image file: WNAS.sys
Image path: \SystemRoot\system32\DRIVERS\WNAS.sys
Image name: WNAS.sys
Timestamp: Wed Apr 18 01:20:12 2007 (4625B89C)
CheckSum: 0000B659
ImageSize: 0000AF80
Translations: 0000.04b0 0000.04e0 0409.04b0 0409.04e0
Alright...no symbols for WNAS means that we are gonna have to do assembly debugging. I know that the WRITE_PORT_ULONG is being imported from 'HAL.DLL'. (FYI: If you're not a driver guru, HAL stands for 'hardware abstraction layer'). HAL.DLL is a Microsoft DLL.
From above, I know the WNAS driver is loaded at address 'baf70000' and it uses HAL.DLL to call function WRITE_PORT_ULONG. Let's take a look at the memory at 0xbaf78c00 (which is the start address of baf70000 + offset 8c00, remember 18C00 from above minus the image base equals 8c00). (see memory window screenshot below). Aha, I see that address 0xbaf78c00 is a pointer to WRITE_PORT_ULONG. Now all we have to do is see who dereferences that pointer to call into that function.
Let's use the '#' WinDbg command to search for the disassembly pattern "baf78c00". The number sign (#) command searches for the specified pattern in the disassembly code.
If you previously used the # command and you omit Address, the search begins where the previous search ended. This command works by searching the disassembled text for the specified pattern. You can use this command to find register names, constants, or any other string that appears in the disassembly output. You can repeat the command without the Address parameter to find successive occurrences of the pattern.
From above (see dumpbin command), we see our code section was loaded at offset 480, so we will start our search at baf70480 (baf70000 which is the start address of WNAS + 480 which is the offset of the code section)
Let's see if we can find some sort of a pointer being dereferenced...
Code:
kd> # "baf78c00" baf70480
WNAS+0x6f7d:
baf76f7d ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
Eurika! At address 'baf76f7d', we are deferencing a pointer to call into HAL.DLL to do a WRITE_PORT_ULONG function call. Let's set a breakpoint there!
Code:
kd> bp baf76f7d
Are there any other places in the WNAS driver that call WRITE_PORT_ULONG? Let's set breakpoints in those locations as well.
Code:
kd> # "baf78c00"
WNAS+0x6fae:
baf76fae ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
kd> bp baf76fae
kd> # "baf78c00"
WNAS+0x7386:
baf77386 ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
kd> bp baf77386
kd> # "baf78c00"
WNAS+0x8c00:
baf78c00 1063a5 adc byte ptr [ebx-5Bh],ah
kd> bp baf78c00
Hey, that last one does not look like a pointer is being dereferenced. I wonder what's happening there? Ah, of course, look at the address. It's the 'baf78c00' address itself. We don't really care about this.
At this point, it looks like we have three breakpoints of interest: Let's use the "bl" command to to list the breakpoints.
Code:
kd> bl
0 e baf76f7d 0001 (0001) WNAS+0x6f7d
1 e baf76fae 0001 (0001) WNAS+0x6fae
2 e baf77386 0001 (0001) WNAS+0x7386
Alright, now let's hit 'g' for go (or continue) and see if we get lucky.
Code:
kd> g
Almost instantly, I break back into WinDbg (which means that we hit one of the breakpoints we just set, inside WNAS, and we are ready to call WRITE_PORT_ULONG with some value)...
Code:
Breakpoint 2 hit
WNAS+0x7386:
baf77386 ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
The "t" command executes a single instruction or source line and optionally displays the resulting values of all registers and flags. When subroutine calls or interrupts occur, each of their steps is also traced.
The "p" command executes a single instruction or source line and optionally displays the resulting values of all registers and flags. When subroutine calls or interrupts occur, they are treated as a single step.
The "r" command displays or modifies registers, floating-point registers, flags, pseudo-registers, and fixed-name aliases.
If two dollar signs ( $$ ) appear at the start of a command, then the rest of the line is treated as a comment, unless the comment is terminated by a semicolon.
Code:
kd> t
hal!WRITE_PORT_ULONG:
80a56310 8b542404 mov edx,dword ptr [esp+4]
kd> p
hal!WRITE_PORT_ULONG+0x4:
80a56314 8b442408 mov eax,dword ptr [esp+8]
kd> p
hal!WRITE_PORT_ULONG+0x8:
80a56318 ef out dx,eax <<<<<<<<<<<<<<<< Hardware write happened here. In this case, we wrote the value 'x0007ff7f' to port 'x00001064'
kd> p
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> r
eax=0007ff7f ebx=00008100 ecx=0000010c edx=00001064 esi=baf78e80 edi=888f4e90
eip=80a56319 esp=b912dbd4 ebp=b912dbf0 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> $$ bottom hard drive led turned from red to off
The bottom (aka forth) hard drive LED just went from red to off. That means that when writing 'x0007ff7f' to port 'x1064', it turns off the hard drive LEDs. Cool! Let's see if we can find some others. Remember we hit "g" to go on and continue...
Code:
kd> g
Once again, almost as soon as I hit 'g' to continue, I broke back into WinDbg
Code:
Breakpoint 2 hit
WNAS+0x7386:
baf77386 ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
kd> t
hal!WRITE_PORT_ULONG:
80a56310 8b542404 mov edx,dword ptr [esp+4]
kd> p
hal!WRITE_PORT_ULONG+0x4:
80a56314 8b442408 mov eax,dword ptr [esp+8]
kd> p
hal!WRITE_PORT_ULONG+0x8:
80a56318 ef out dx,eax <<<<<<<<<<<<<<<< Hardware write happened here. In this case, we wrote the value 'x0007ff7d' to port 'x00001064'
kd> t
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> r
eax=0007ff7d ebx=00008103 ecx=00000101 edx=00001064 esi=baf78e80 edi=888f4e90
eip=80a56319 esp=b912dbd4 ebp=b912dbf0 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> $$ third hard drive LED turned blue
The third hard drive LED just went from off to blue. That means that when writing 'x0007ff7d' to port 'x1064', it turns on the third hard drive LED and has to rest powered off. Cool! Let's see if we can find some others. Remember we hit "g" to go on and continue... (you get the idea)
Code:
Breakpoint 19 hit
WNAS+0x7386:
baf77386 ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
kd> t
hal!WRITE_PORT_ULONG:
80a56310 8b542404 mov edx,dword ptr [esp+4]
kd> p
hal!WRITE_PORT_ULONG+0x4:
80a56314 8b442408 mov eax,dword ptr [esp+8]
kd> p
hal!WRITE_PORT_ULONG+0x8:
80a56318 ef out dx,eax
kd> p
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> $$ third LED went from blue to pink
kd> r
eax=0007fe7d ebx=00008103 ecx=00000108 edx=00001064 esi=baf78e80 edi=888f4e90
eip=80a56319 esp=b912dbd4 ebp=b912dbf0 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> p
WNAS+0x738c:
baf7738c 33ff xor edi,edi
kd> g
Breakpoint 19 hit
WNAS+0x7386:
baf77386 ff15008cf7ba call dword ptr [WNAS+0x8c00 (baf78c00)]
kd> t
hal!WRITE_PORT_ULONG:
80a56310 8b542404 mov edx,dword ptr [esp+4]
kd> p
hal!WRITE_PORT_ULONG+0x4:
80a56314 8b442408 mov eax,dword ptr [esp+8]
kd> p
hal!WRITE_PORT_ULONG+0x8:
80a56318 ef out dx,eax
kd> p
hal!WRITE_PORT_ULONG+0x9:
80a56319 c20800 ret 8
kd> $$ third LED went from pink to red
kd> r
eax=0007fe7f ebx=00008100 ecx=00000101 edx=00001064 esi=baf78e80 edi=888f4e90
eip=80a56319 esp=b912dbd4 ebp=b912dbf0 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
So you can see how with WinDbg, you can do assembly level programming to reverse engineer the (in this case) writes to hardware.
Interested in helping out?
Build or
buy a cable with serial functionality and join the fun. Report your results to this forum and help us out.