Chinaunix首页 | 论坛 | 博客
  • 博客访问: 28955
  • 博文数量: 5
  • 博客积分: 550
  • 博客等级: 中士
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-01 21:54
文章分类

全部博文(5)

文章存档

2011年(1)

2010年(3)

2009年(1)

我的朋友

分类: WINDOWS

2010-09-28 01:10:03

Most Common File Systems 

Sat Aug 23 20:59:30 2003 

FAT = File Allocation Table 
The file system that is used/Or ordinarily designed for floppies and used by DOS, W 3.x, W95, Windows NT and OS/2. In technical terms refered as FAT12 and FAT16 in which 12 and 16 standing for bits. A FAT directory holds info such as name, file size, date & time stamp, the starting cluster number and the file attributes like (archive, hidden, system etc.). It's file system can support up to 65,525 clusters and is limited to 2 GB. Works best on small 500mb drives because of the cluster size. It seems to be about 2% faster than FAT32 and NTFS but windows is faster if confined to a small area. FAT performance drops off after 400mb's on up. 


FAT32 = File Allocation Table 32 such as W-95b 
FAT32 will not recognize FAT or NTFS volumes of other operating systems--so you can't use them. It supports drives up to 2 terabytes. It uses smaller clusters (e.g. 4k clusters up to 8 gigs). 


So--What's the Difference (FAT12/16 or FAT32) 
Remember that DOS 6.x or even versions of Windows prior to SR2 won't recognize the front end of a FAT32 partition. So if you must run the occasional old DOS app, move it into a FAT16 drive partition and then restart from an old DOS boot diskette. FAT16 does not support partitions larger than 2GB. FAT32 is an improvement, as it supports drives up to 2 Terabytes in size, and cluster sizes are 4K for partitions smaller than 8GB. So, if you can get FAT32 on the drive, it will work. 

Fat12/16 and Fat32 is a Partition size/cluster size issue. FAT32 solves this problem by reducing to 4KB the default file cluster size for partitions between 260MB and 8GB. (Drives or partitions under 260MB use .5KB clusters.) Up to 16GB, FAT32's cluster size is 8KB; to 32GB, it's 16KB; and for partitions of 32GB and greater, the cluster size holds steady at 32KB. FAT32 adds a few other improvements. The root directory on a FAT32 drive is now an ordinary cluster chain, so it can be located anywhere on the drive. This removes FAT16's previous limitation of 512 root directory entries. In addition, the boot record on FAT32 drives has been expanded to allow a backup of critical data structures. This makes FAT32 drives less susceptible to failure. FAT32 partitions are also invisible to other operating systems, including other versions of Windows. To access a FAT32 partition from a boot floppy, you must create an SR2 start-up disk. You won't see your C: drive if you boot from an older Win95 or DOS start-up disk. I


FAT32/FAT32x 
In the beginning, DOS and Windows systems used FAT12/FAT16. But when drives got larger, (meaning over 2 gigb) along came FAT32 and now FAT32x. 

FAT16 was limited to 32 MB drives (hi hi hi) and it was updated over the years (by manipulated sector translation) until it became necessary to increase its basic structure from 16 to 32. FAT32 can safely handle drives up to 2 Terabytes--Err, they say--but it has this problem over 8.4 GB. 

The ("x") refers to eXtensions to the FAT32 specification, because with the advent of drives exceeding 8.4 Gig, a new limitation was reached. Prior to this, all drives used some form of CHS (Cylinder Head Sector) translation. Under this scheme every sector was given three numbers. In anticipation of this limitation being exceeded, manufacturers developed Logical Block Addressing (LBA). With LBA, each sector is given a unique number depending on the BIOS. 

To expand you knowledge base, let's talk about scenarios. Windows 95 and 98 comes with an enhanced 32-bit driver that is LBA compliant (OK) but a certain BIOS limitation can blow out Windows, but run ok in DOS. Why??, DOS does not use the Windows enhanced driver -- that's all. Conclusion, if you try playing with the DOS command FDISK in DOS Mode and then use it from inside a Windows Dos Prompt and funny stuff happens THEN you know to beware of DOS mode, even though there's 4 different versions of FDISK. 

FAT32X is a form of FAT32 created by the Windows Fdisk utility when partitions over 8 GB in size are created, and the 1024 cylinder threshold of the disk is passed. The File Allocation Table is moved to the end of the disk in these cases. 

As many of users have found, there seems to be a "mystical" limit to how big a hard drive can be used by DOS. At first glance this limit seems arbitrary and can be frustrating, especially with the sudden glut of 8GB+ drives on the market. There is, however, a valid reason behind it. 

On most standard IDE drives (SCSI are different but similar rules apply) it is normal to have 16 heads and 63 sectors per track. Cylinders increase as drive size increases. We are seeing drives with as high as 24000 cylinders or as low as 50. To figure the drive's capacity, multiply the cylinder, head, and sector numbers together. Divide the product by 2048. As a formula it looks like this: 

(cylinders * heads * sectors) / 2048 = megabytes 

This will give you a number in megabytes that are equal to the size of the drive. The system uses these numbers to help it when reading and writing to the disk. 

The first limit comes directly from how these numbers relate to the system. The system BIOS's INT13h interface allows for a maximum of 1024 cylinders, 255 heads, and 63 sectors per track. The standard IDE interface allows for a maximum of 65,536 cylinders, 16 heads, and 63 sectors. In order to satisfy the limits of both of these numbers, the minimum highest common number for each is used. This produces a maximum number of 1024 cylinders, 16 heads, and 63 sectors (504 MB per the calculation above). 

That limit quickly became too restrictive and a work around method was developed. It is now possible to "translate" a drive by multiplying the number of heads to reduce the number of cylinders. For instance, if I had a drive that was 2046 cylinders, 16 heads, and 63 sectors, I could translate it by halving the number of cylinders and doubling the number of heads. This results in a drive that has 1023 cylinders, 32 heads, and 63 sectors. 

By using a translator between the IDE interface and the BIOS INT13h interface, we can accomplish this translation and satisfy both limits. My IDE drive will still have the same physical number of cylinders, heads, and sectors that fit within its limits, but the numbers reported to the BIOS INT13h interface will be translated. This changes our limit to 1024 cylinders, 255 heads, and 63 sectors (8GB per the calculation above) or simply the limits BIOS have in the first place. We are now capable of working with anything up to 8GB. 

There is another place that this limit can be found. Every partition table in a PC is set up with the same parameters. An entry for a single partition is 16 bytes long. Of those 16 bytes, three re dedicated to holding the beginning cylinder, head, and sector of a partition and three are dedicated to holding the end cylinder, head, and sector of a partition. A single byte can hold a number up to 255. If this number where strictly adhered to this would leave a limit of 255 cylinders, 255 heads, and 255 sectors. Of the heads byte, this is true. The other two bytes are manipulated slightly to allow for different numbers. The sector number is held in a six digit binary number and the cylinders in a 10 digit binary number. To fit this into the byte structure (remember 8 bits to a byte), the first two digits of the cylinder number are chopped off and put on the front of the sector number. Learning to interpret the numbers can be interesting, but it effectively gives us a limit of 1023 cylinders, 255 heads, and 63 

But the limit persists. This is because all logical addressing is done by operating systems. Windows 95, Windows NT 4.0, and OS/2 Warp have systems that allow them to address the drive in a logical manner. By doing this, all of these operating systems can break the 8GB barrier. DOS and Windows 3.x don't have this ability. They still rely on the traditional cylinder, head, and sector addressing. For this reason any program that runs under these operating systems is also limited to 8GB unless it has its own system for using logical addressing. 

Working in FAT32x partitions is essentially the same as working in FAT32 partitions. However, when attempting to manipulate a FAT32x partition, problems may occur. Procedures such as copying, imaging, resizing, and moving FAT32x partitions require different methods than those used for FAT32 partitions. 

Many new computers have pre-installed FAT32x partitions. This has created numerous problems for individuals wishing to modify their partitions on their new systems. FAT32x partitions have a different file system flag in the partition table. Sometimes a FAT32x partition is erroneously created entirely within 1024 cylinders. This can be corrected, in some cases, by using a disk editing utility. 


VFAT (Virtual File Allocation Table) 
A protected-mode version of the FAT file system, used by Windows 95. It is compatible with the FAT system, the main difference being support for long filenames. 


NTFS (New Technology File System) 
This systems structure is the (MFT) or master file table. It uses too much space to use on a (e.g. 400mb) hard-drive because it keeps multiple copies of files in the MFT to protect against data loss. It also uses clusters to store data in small noncontiguous clusters and isn't broken up resulting in good performance on large hard-drives. It also supports Hot Fixing where bad sectors are automatically detected and marked. 


HPFS (High Performance File System) 
This system sorts the directory based on names and is better organized, is faster and is a better space saver. It allocates data to sectors instead of clusters, organized into 8mb bands. This banding improves performance because the read/write heads don't have to return to track zero each time for access. NetWare File System: This is quick because Novell developed it for NetWare servers being NetWare 3.x and 4.x partitions. Linux Ext2: This is also quick because it is a developed version of UNIX. The Linux Ex12 volume supports up to 2 terabytes. 


Inside Windows 2000/XP Interrupt 

Sat Aug 23 21:44:29 2003 

Inside Windows 2000/XP Interrupt 
Xinhai Kang, kanghai2k@hotmail.com 

This article is not related to the Inside series of Mark Russinovich at all, though everyone knows about him and his series of articles. I am just a minor compare to him, . I don’t want to tell you how to understand interrupt of IA32, I don’t want to tell you most general problems that you can get to know from DDK or some other documentations either. What I want to tell you here something new, something you may never heard of... 

I am not good at writing, let’s go through this article with an example, OK? 

Don’t tell me you never use Windows kernel debugger because you use SoftICE only. 

Let’s see current interupt setting in the system. Welcome on board! 

kd> .load kdex2x86 
Loaded kdex2x86 extension DLL 

kd> !idt 
IDT for processor #0 
    00: 804625e6 (ntoskrnl!KiTrap00) 
    01: 80462736 (ntoskrnl!KiTrap01) 
... ... 
    3c: 81819dc4 (Vector:3c,Irql:f,SyncIrql:1a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042MouseInterruptService(edc86d10))
    3d: 80460c92 (ntoskrnl!KiUnexpectedInterrupt13) 
    3e: 8184d984 (Vector:3e,Irql:d,SyncIrql:d,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:atapi!ScsiPortInterrupt(bff89e28)) 
... ... 

Vector 3e, that is IRQ 15 (0x0e), is the primary IDE channel interrupt, let’s take it as the example. Don’t ask me if don’t translate interrupt vector to IRQ#. 

It said IRQ 15 is handled by atapi!ScsiPortInterrupt (YES, it isn’t scsiport!ScsiPortInterrupt, this is different from NT4, atapi in 2K/XP is now a complete scsiport driver instead of a miniport driver), ok, let’s set a breakpoint here… 

kd> bp atapi!ScsiPortInterrupt 
kd> g 
Breakpoint 0 hit 
atapi!ScsiPortInterrupt: 

We get an interrupt, let ‘s watch the current call stack. 

kd> kv 
ChildEBP RetAddr  Args to Child              
8046fd0c 8046586a 8184d948 81894030 81894102 atapi!ScsiPortInterrupt (FPO: [2,0,2]) 
8046fd0c 80067a5a 8184d948 81894030 81894102 ntoskrnl!KiInterruptDispatch+0x2a (FPO: [0,2] TrapFrame @ 8046fd24) 
8046fd94 80460b59 0000000e 00000000 00000000 halacpi!HalProcessorIdle+0x2 (FPO: [0,0,0]) 
ffdff800 8047e684 00000001 00000000 00001d63 ntoskrnl!KiIdleLoop+0x10 
8047e684 ffdff800 80431669 00000000 0000170c ntoskrnl!KiTimerExpireDpc+0x4 
... ... 

Hmm, the atapi!ScsiPortInterrupt was called from ntoskrnl!KiInterruptDispatch, but we cannot get any useful information before KiInterruptDispatch was called. Anyway, let’s see what happened in KiInterruptDispatch. 

kd> u ntoskrnl!KiInterruptDispatch 
ntoskrnl!KiInterruptDispatch: 
80465840 ff0560f5dfff     inc     dword ptr [ffdff560] 
80465846 8bec             mov     ebp,esp 
80465848 8b4724           mov     eax,[edi+0x24] 
8046584b 8b4f29           mov     ecx,[edi+0x29] 
8046584e 50               push    eax 
8046584f 83ec04           sub     esp,0x4 
80465852 54               push    esp 
80465853 50               push    eax 
80465854 51               push    ecx 
80465855 ff1540054080 call dword ptr [ntoskrnl!_imp__HalBeginSystemInterrupt (80400540)] 
8046585b 0bc0             or      eax,eax 
8046585d 741a             jz     ntoskrnl!KiInterruptDispatch+0x39 (80465879) 
8046585f 8b771c           mov     esi,[edi+0x1c] 
80465862 8b4710           mov     eax,[edi+0x10] 
80465865 50               push    eax 
80465866 57               push    edi 
80465867 ff570c           call    dword ptr [edi+0xc] 
8046586a fa               cli 
8046586b ff1544054080 call dword ptr [ntoskrnl!_imp__HalEndSystemInterrupt (80400544)] 
80465871 e9e0c7ffff       jmp     ntoskrnl!Kei386EoiHelper (80462056) 
... ... 

Note the line call dword ptr [edi+0xc] here. That’s exactly where atapi!ScsiPortInterrupt was being called. But what edi is here? We cannot get any clue from the above code and it’s obvious that edi was set before KiInterruptDispatch was called. I was lost here when I first tried to understand Windows interrupt. But I noticed that KiInterruptDispatch will be called during every interrupt handling, and edi is different for different ISRs, and [edi+0xc] is exactly the ISR listed by !idt above. 

kd> r 
eax=81894030 ebx=0000000e ecx=00000002 edx=0000132b esi=8184dbac edi=8184d948 
eip=80465867 esp=8046fd14 ebp=8046fd24 iopl=0         nv up ei pl nz na pe nc 
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000202 

kd> db edi 
8184d948  16 00 e4 01 4c d9 84 81-4c d9 84 81 28 9e f8 bf  ....L...L...(... 
8184d958  30 40 89 81 00 00 00 00-00 00 00 00 ac db 84 81  0@.............. 
8184d968  40 58 46 80 3e 00 00 00-0d 0d 00 01 00 00 00 00  @XF.>........... 
8184d978  01 00 00 00 00 00 00 00-00 00 00 00 54 55 53 56  ............TUSV 
8184d988  57 83 ec 54 8b ec 89 44-24 44 89 4c 24 40 89 54  W..T...D$D.L$@.T 
8184d998  24 3c f7 44 24 70 00 00-02 00 0f 85 13 01 00 00  $<.D$p.......... 
8184d9a8  66 83 7c 24 6c 08 74 23-8c 64 24 50 8c 5c 24 38  f.|$l.t#.d$P.\$8 
8184d9b8  8c 44 24 34 8c 6c 24 30-bb 30 00 00 00 b8 23 00  .D$4.l$0.0....#. 

I guess edi must be something related to KINTERRUPT, which is the only object we can communicate with kernel. I tried to find the structure of KINTERRUPT, and fortunately I found it. 

The structure of KINTERRUPT listed below was copied from Thanks! 

typedef struct _KINTERRUPT { 
    /*00*/CSHORT              Type; 
    /*02*/USHORT              Size; 
    /*04*/LIST_ENTRY          InterruptListEntry; 
    /*0C*/PKSERVICE_ROUTINE   ServiceRoutine; 
    /*10*/PVOID               ServiceContext; 
    /*14*/KSPIN_LOCK          SpinLock; 
    /*18*/ULONG               TickCount; 
    /*1C*/PKSPIN_LOCK         ActualLock; 
    /*20*/PVOID               DispatchAddress; 
    /*24*/ULONG               Vector; 
    /*28*/KIRQL               Irql; 
    /*29*/KIRQL               SynchronizeIrql; 
    /*2A*/BOOLEAN             FloatingSave; 
    /*2B*/BOOLEAN             Connected; 
    /*2C*/UCHAR               Number; 
    /*2D*/UCHAR               ShareVector; 
    /*30*/KINTERRUPT_MODE     Mode; 
    /*34*/ULONG               ServiceCount; 
    /*38*/ULONG               DispatchCount; 
    /*3C*/ULONG               DispatchCode[106]; 
} KINTERRUPT, *PKINTERRUPT; 

Note there is a field DispatchCode in it! 


kd> ? edi+0x3c 
Evaluate expression: -2121999996 = 8184d984 

I cannot get more information by back tracing, I decided to check some CPU information. First, I checked IDT register and try to find what’s the IDT is now… 

kd> dq idtr+0x3e*8 
800365f0  81848e00`0008d984 80468e00`00080ca6 
80036600  80468e00`00080cb0 80468e00`00080cba 

It’s easy to get Segment Selector = 0008 and Offset = 8184d984. That is, the true ISR recognized by the CPU is at 8: 8184d984. Where is it? 

kd> ? edi+0x3c 
Evaluate expression: -2121999996 = 8184d984 

Right, here it is. It’s KINTERRUPT[0x3c], that is DispatchCode. Ok, I am pretty sure edi is a pointer to the KINTERRUPT now. I can read what’s a Windows interrupt handler looks like now… 

kd> u edi+0x3c l 110 
8184d984 54               push    esp 
8184d985 55               push    ebp 
8184d986 53               push    ebx 
8184d987 56               push    esi 
8184d988 57               push    edi 
8184d989 83ec54           sub     esp,0x54 
8184d98c 8bec             mov     ebp,esp 
8184d98e 89442444         mov     [esp+0x44],eax 
8184d992 894c2440         mov     [esp+0x40],ecx 
8184d996 8954243c         mov     [esp+0x3c],edx 
8184d99a f744247000000200 test    dword ptr [esp+0x70],0x20000 
8184d9a2 0f8513010000     jne     8184dabb 
8184d9a8 66837c246c08     cmp     word ptr [esp+0x6c],0x8 
8184d9ae 7423             jz      8184d9d3 
8184d9b0 8c642450         mov     [esp+0x50],fs 
8184d9b4 8c5c2438         mov     [esp+0x38],ds 
8184d9b8 8c442434         mov     [esp+0x34],es 
8184d9bc 8c6c2430         mov     [esp+0x30],gs 
8184d9c0 bb30000000       mov     ebx,0x30 
8184d9c5 b823000000       mov     eax,0x23 
8184d9ca 668ee3           mov     fs,bx 
8184d9cd 668ed8           mov     ds,ax 
8184d9d0 668ec0           mov     es,ax 
8184d9d3 648b1d00000000   mov     ebx,fs:[00000000] 
8184d9da 64c70500000000ffffffff mov dword ptr fs:[00000000],0xffffffff 
8184d9e5 895c244c         mov     [esp+0x4c],ebx 
8184d9e9 81fc00000100     cmp     esp,0x10000 
8184d9ef 0f829e000000     jb      8184da93 
8184d9f5 c744246400000000 mov     dword ptr [esp+0x64],0x0 
8184d9fd fc               cld 
8184d9fe f60550f0dfffff   test    byte ptr [ffdff050],0xff 
8184da05 750c             jnz     8184da13 
8184da07 bf48d98481       mov     edi,0x8184d948 
8184da0c e92f7ec1fe       jmp     ntoskrnl!KiInterruptDispatch (80465840) 
8184da11 8bff             mov     edi,edi 
8184da13 f7457000000200   test    dword ptr [ebp+0x70],0x20000 
8184da1a 7509             jnz     8184da25 
8184da1c f7456c01000000   test    dword ptr [ebp+0x6c],0x1 
8184da23 74e2             jz      8184da07 
8184da25 0f21c3           mov     ebx,dr0 
8184da28 0f21c9           mov     ecx,dr1 
8184da2b 0f21d7           mov     edi,dr2 
8184da2e 895d18           mov     [ebp+0x18],ebx 
8184da31 894d1c           mov     [ebp+0x1c],ecx 
8184da34 897d20           mov     [ebp+0x20],edi 
8184da37 0f21db           mov     ebx,dr3 
8184da3a 0f21f1           mov     ecx,dr6 
8184da3d 0f21ff           mov     edi,dr7 
8184da40 895d24           mov     [ebp+0x24],ebx 
8184da43 894d28           mov     [ebp+0x28],ecx 
8184da46 bb00000000       mov     ebx,0x0 
8184da4b 897d2c           mov     [ebp+0x2c],edi 
8184da4e 0f23fb           mov     dr7,ebx 
8184da51 648b3d20000000   mov     edi,fs:[00000020] 
8184da58 8b9ff8020000     mov     ebx,[edi+0x2f8] 
8184da5e 8b8ffc020000     mov     ecx,[edi+0x2fc] 
8184da64 0f23c3           mov     dr0,ebx 
8184da67 0f23c9           mov     dr1,ecx 
8184da6a 8b9f00030000     mov     ebx,[edi+0x300] 
8184da70 8b8f04030000     mov     ecx,[edi+0x304] 
8184da76 0f23d3           mov     dr2,ebx 
8184da79 0f23d9           mov     dr3,ecx 
8184da7c 8b9f08030000     mov     ebx,[edi+0x308] 
8184da82 8b8f0c030000     mov     ecx,[edi+0x30c] 
8184da88 0f23f3           mov     dr6,ebx 
8184da8b 0f23f9           mov     dr7,ecx 
8184da8e e974ffffff       jmp     8184da07 
8184da93 8b442464         mov     eax,[esp+0x64] 
8184da97 b910000000       mov     ecx,0x10 
8184da9c 8bd4             mov     edx,esp 
8184da9e c1e010           shl     eax,0x10 
8184daa1 64031508000000   add     edx,fs:[00000008] 
8184daa8 89442464         mov     [esp+0x64],eax 
8184daac 668ed1           mov     ss,cx 
8184daaf 8be2             mov     esp,edx 
8184dab1 8bea             mov     ebp,edx 
8184dab3 e945ffffff       jmp     8184d9fd 
8184dab8 8d4900           lea     ecx,[ecx] 
8184dabb 8b8584000000     mov     eax,[ebp+0x84] 
8184dac1 8b9d88000000     mov     ebx,[ebp+0x88] 
8184dac7 8b4d7c           mov     ecx,[ebp+0x7c] 
8184daca 8b9580000000     mov     edx,[ebp+0x80] 
8184dad0 66894550         mov     [ebp+0x50],ax 
8184dad4 66895d30         mov     [ebp+0x30],bx 
8184dad8 66894d34         mov     [ebp+0x34],cx 
8184dadc 66895538         mov     [ebp+0x38],dx 
8184dae0 e9dbfeffff       jmp     8184d9c0 
8184dae5 6a12             push    0x12 
8184dae7 e83d61fcff       call    81813c29 
8184daec 90               nop 
8184daed cc               int     3 
8184daee cc               int     3 
8184daef cc               int     3 
8184daf0 cc               int     3 
8184daf1 cc               int     3 
8184daf2 cc               int     3 
8184daf3 55               push    ebp 
8184daf4 8bec             mov     ebp,esp 
8184daf6 669c             pushf 
8184daf8 fa               cli 
8184daf9 0f20e0           mov     eax,cr4 
8184dafc f7450801000000   test    dword ptr [ebp+0x8],0x1 
8184db03 7405             jz      8184db0a 
8184db05 83c801           or      eax,0x1 
8184db08 eb03             jmp     8184db0d 
8184db0a 83e0fe           and     eax,0xfffffffe 
8184db0d 0f22e0           mov     cr4,eax 
8184db10 669d             popf 
8184db12 33c0             xor     eax,eax 
8184db14 8be5             mov     esp,ebp 
8184db16 5d               pop     ebp 
8184db17 c20400           ret     0x4 

Heihei, it’s always boring to read assembly. 

Ok, we should have some conclusion now. As we all know that we call IoConnectInterrupt in a driver to register our ISR which returns a KINTERRUPT object. When IoConnectInterrupt is called, Windows will copy a piece of code into this object (the DispatchCode field) as the entry point of the ISR and set up the IDT entry. Simple? Easy? Yes, that’s all. 

A last word, if you tried to trace your Windows, you may get something different, that’s because I tested it in a standard PC HAL Windows 2000. If you have an ACPI HAL, that would be different, for example, you may see HAL!…. instead of ntoskrnl!KiInterruptDispatch. But the KINTERRUPT is the same. 


剖析Windows NT/2000内核对象组织 

Sat Aug 23 21:46:40 2003 

剖析Windows NT/2000内核对象组织 
作者:WebCrazy 

                  ( 

    对象管理器在Windows NT/2000内核中占了极其重要的位置,其一个最主要职能是组织管理系统内核对象。在Windows NT/2000中,内核对象管理器大量引入了C++面向对象的思想,即所有内核对象都封装在对象管理器内部,除对象管理器自己以外,对其他所有想引用内核对象结构成员的子系统都是不透明的,也即都需通过对象管理器访问这些结构。Microsoft极力推荐内核驱动代码遵循这一原则(用户态代码根本不能直接访问这些数据),她提供了一系列以Ob开头的例程供我们使用。但是也不是说只有对象管理器即这些函数才能访问这些数据。我下面提供的代码则直接存取这些结构,这种情况下,我们只能祈祷Microsoft永远保持这些对象头,对象体结构的不变,这一般不大可能,所以我提供的代码只供学习Windows NT/2000内核之用,无其它实际应用意义。 

    先谈谈内核已命名对象吧,命名对象存于系统全局命名内核区,与传统的DOS目录与文件组织方式相似,对象管理器也采用树状结构管理这些对象,这样即可快速检索内核对象。这个优点很容易体现,对象检索速度快,这样会大大提高系统性能,举个例子吧:譬如进程A某个时刻已经使用某个文件,即A在内核中拥有了指向这个文件的一个内核对象(FILE_OBJECT),而如果这时另一进程B也试图访问这个文件,则B通过CreateFile API等打开这个文件时,系统指引对象管理器查找对象存储空间,如已存在此对象,则查看对象头,是否已独占访问,假如是的话,则会出现失败,否则的话,增加此对象的引用计数或句柄计数。系统中这种搜索对象的情况无处不在,因为Windows NT/2000中将所有的需SECURITY_DESCRIPTOR等 结构进行保护的可操作的数据结构都当作对象处理,如常见的进程对象(EPROCESS/KPEB)、线程对象(ETHREAD/KTEB)、驱动程序对象(DRIVER_OBJECT)等等。当然这种树状结构组织内核已命名对象,还有另一个优点,就是使所有已命名对象组织的十分有条理,如设备对象处于\Device下,而对象类型名称处于\ObjectTypes下等等。再者这样也能达到用户态进程仅能访问\??与\BaseNamedObjects下的对象,而内核态代码则没有任何限制的

    kd> !object \ 
    Object: 8148e210  Type: (814c5820) Directory 
        ObjectHeader: 8148e1f8 
        HandleCount: 0  PointerCount: 39 
        Directory Object: 00000000  Name: \ 
        99 symbolic links snapped through this directory 
        HashBucket[ 00 ]: 8148a350 Directory 'ArcName' 
                          814a8f10 Device 'Ntfs' 
        HashBucket[ 01 ]: e2390040 Port 'SeLsaCommandPort' 
        HashBucket[ 03 ]: e1012030 Key '\REGISTRY' 
        HashBucket[ 06 ]: e1394560 Port 'XactSrvLpcPort' 
        HashBucket[ 07 ]: e13682e0 Port 'DbgUiApiPort' 
        HashBucket[ 09 ]: 84305760 Directory 'NLS' 
                        . 
                        . 
                        . 

    kd> !object 814a8f10 
    Object: 814a8f10  Type: (814b5ac0) Device 
        ObjectHeader: 814a8ef8 
        HandleCount: 0  PointerCount: 2 
        Directory Object: 8148e210  Name: Ntfs 

    实现代码如下: 

    //----------------------------------------------- 
    // 
    // Dump Windows 2000 Kernel Object 
    // Only test on Windows 2000 Server Chinese Edition 
    // Build 2195(Free)!Programmed By WebCrazy 
    // (tsu00@263.net ) on 11-04-2000! 
    // Welcome to ! 
    // 
    //----------------------------------------------- 

    ULONG ObpRootDirectoryObject=0x8148e210;  //fetch from symbol file 

    void DumpDirectoryObject(PVOID DirectoryObject) 
    { 
       ULONG HashBucket; 
       ULONG *Hash; 

       for(HashBucket=0;HashBucket<=0x24;HashBucket++) 
       { 
          Hash=(ULONG *)((ULONG)DirectoryObject+HashBucket*4); 
          if(*Hash==0) continue; 

          DbgPrint("\n    HashBucket[%02X]\n",HashBucket); 
          do 
          { 
              PUNICODE_STRING ObName,ObTypeName; 
              PVOID Object,ObPreHeader,ObStandardHeader; 

              Hash=(ULONG *)(*Hash); 
              Object=(PVOID)(*(ULONG *)((ULONG)Hash+4)); 
              ObPreHeader=(PVOID)((ULONG)Object-0x28); 
              ObStandardHeader=(PVOID)((ULONG)Object-0x18); 

              ObName=(PUNICODE_STRING)((ULONG)ObPreHeader+4); 
              DbgPrint("\tDump Object:%08X\n",(ULONG)Object); 
              DbgPrint("\t  Name:%S",ObName->Buffer); 

              ObTypeName=(PUNICODE_STRING)((ULONG)(*(ULONG *)((ULONG)ObStandardHeader+8))+0x40); 
              DbgPrint("\t  Type:%S(%08X)\n",ObTypeName->Buffer, 
                            *(ULONG *)((ULONG)ObStandardHeader+8)); 

              DbgPrint("\t  PointerCount:%d  HandleCount:%d\n",*(ULONG *)ObStandardHeader, 
                            *(ULONG *)((ULONG)ObStandardHeader+4)); 

          }while(*Hash!=0); 
       } 
    } 

    void DumpObject(PVOID Object) 
    { 

       PUNICODE_STRING ObName,ObTypeName; 
       UNICODE_STRING temp; 
       PVOID ObPreHeader=(PVOID)((ULONG)Object-0x28), 
            ObStandardHeader=(PVOID)((ULONG)Object-0x18); 

       if(((USHORT)NtBuildNumber)!=2195){ 
           DbgPrint("Only test on Windows 2000 Server Build 2195!\n"); 
           return; 
       } 

       ObName=(PUNICODE_STRING)((ULONG)ObPreHeader+4); 
       DbgPrint("  Dump Object:%08X\n    Name:%S",(ULONG)Object,ObName->Buffer); 

       ObTypeName=(PUNICODE_STRING)((ULONG)(*(ULONG *)((ULONG)ObStandardHeader+8))+0x40); 
       DbgPrint("    Type:%S(%08X)\n",ObTypeName->Buffer,*(ULONG *)((ULONG)ObStandardHeader+8)); 

       DbgPrint("    PointerCount:%d  HandleCount:%d\n",*(ULONG *)ObStandardHeader, 
                     *(ULONG *)((ULONG)ObStandardHeader+4)); 

       RtlInitUnicodeString(&temp,L"Directory"); 
       if(!RtlCompareUnicodeString(&temp,ObTypeName,FALSE)) 
          DumpDirectoryObject(Object); 
    } 

    void DumpRootDirectoryObject() 
    { 
       DumpObject((PVOID)ObpRootDirectoryObject); 
    } 

    看过上面Windbg的输出或是用过Softice的objdir命令的人,应该都知道代码的用处,所以我就不加以介绍。这段代码直接读取系统全局命名对象区,没有考虑同步等,而且在引用对象成员时也没有对PointerCount进行加一,另外目前也没有考虑OBJECT_TYPE中定义的CallBack函数,这样在用户驱动代码或操作系统挂接一些额外的应用时,可能会导致错误,不过因为代码中我对对象区的数据结构有了较为清楚的定义,对理解Windows NT/2000的内核对象组织较有帮助,所以我就将它列出(这段代码我理解了很久后才写的)。代码中我使用PVOID指针,且将指针转化为ULONG类型,直接将结构的偏移地址进行相加。不使用如(ULONG *)Ob++这种语法,这样您可以直接从代码中提取一些结构重要成员的偏移,但这也造成代码不够简洁。关于ObpRootDirectoryObject这个最重要的Windows NT/2000内核变量,我直接从Symbols文件中提取,您必须根据实际情况予以调整。或者使用ObReferenceObjectByName获得。 

    其实现在已经有很多应用程序可以实现这种列举对象的功能,除了上面的windbg/i386kd的!object、SoftICE的objdir命令,另外还有DDK中的objdir.exe、Mark Russinovich的winobj.exe等等。如果你搜索命名对象存储区域,就可以实现系统设备(Device)对象与驱动程序(Driver)对象的枚举,即可实现SoftICE中device与driver命令的功能。必须指出的是,不是所有的device对象都位于\Device下,也不是所有的driver对象都位于\Driver下,所以必须通过递归调用DumpDirectoryObject寻找整个空间,我不知道为什么Microsoft不限制device与driver对象的存放位置。 

    以上谈及的命名内核对象可在进程间共享,这也是Windows NT/2000中LPC(Local Procedure Call)的最基本原理,LPC使用到内核中称为Port的内核对象。为什么命名对象能在进程间共享,而以下要谈及的未命名对象却不能在进程间共享呢?通过我的理解,我觉得既然两者都位于系统内核4G内存的高端,理论上应该均可以在进程间共享。只是因为Microsoft提供诸如ObReferenceObjectByName直接根据对象名查找对象,而她提供的ObReferenceObjectByHandle却没有指定进程的参数,所以未命名对象在这种程度上是不能共享的。另外,Microsoft不提供这种参数的另一个最主要的原因我想应该是为了系统更健壮、更稳定。下面我就来说说“句柄”这种管理未命名内核对象的机制吧。 

    为什么要引入“句柄”的概念呢?为防止用户代码对内核对象造成破坏,Windows NT/2000中通过“句柄”这一机制避免将内核对象指针直接返回给用户进程,而“句柄”除了此功能外,它还管理着进程特定拥有的所有内核对象,当然也包含未命名对象。SoftICE中的proc命令加-o选项可以得到特定进程拥有的所有句柄。但在检索某一指定句柄时,SoftICE在使用过程中也感到不便,我曾试着使用Numega提供的KD2SYS.EXE将Windbg Extension DLL转化成SoftICE使用的.SYS文件,但在使用过程中也经常出现问题。后来,在分析了ObReferenceObjectByHandle内核例程后,我在SoftICE中定义了如下的宏(关于ObReferenceObjectByHandle分析过程与代码我不列于此,您可以仔细认真跟踪跟踪): 

    // 定义宏handle 
    // handle  
    // 参数kpeb指定特定进程,handle index是句柄索引 

    :macro handle="addr %1;what (@(@(@(@(@(%1+128)+8)))+(%2>>2)*8))|80000000;." 
    Macro: 'handle' defined 

    // 显示System进程中handle index为4的句柄对象指针与对象类型名 

    :handle dword(@PsInitialSystemProcess) 4 
    The value 8148EBC8 is (a) Kernel Process object (handle=0004) for System(08) 
              --------        ---------------------         ----   -------------     
                 |_句柄头地址         |_句柄对应对象的类型    |      |_指明此句柄隶属System进程 
                                                              |_handle index 

    这个宏我仅在Microsoft Windows 2000 Server Build 2195 Chinese Edition上测试通过。如上显示输出结果,其只显示对象指针与对象类型名。要查看对象更详细的资料您只能深入内核对象头与对象体结构或是使用Windbg的!handle命令。这个宏已经将进程的句柄表的结构说明的很清楚了,而且对象的很多结构在我上面提供的代码中也有应用到,我就不将具体实现代码列出了,建议看看Mark Russinovich的HandleEx。 

    关于标题所提到的Windows NT/2000内核对象组织还有非常多的内容,如内核对象的安全性设置等等,限于水平与精力,我也不可能在此都写出。另外应该提出的是本文未涉及用户模式的GDI对象等,其在组织上也有另一套的方式。在对Windows NT/2000的分析过程中,我初步感觉到Microsoft设计Windows NT/2000的许多先进思想,也越来越体会到分析的难度。很希望您在有所心得后,也能与大家交流交流,或是联系我(tsu00@263.net)! 

参考资料: 
      1.David Solomom《Inside Windows NT,2nd Edition》 
      2.Mark Russinovich相关文档  

探索NTFS 

Sat Aug 23 21:47:54 2003 

探索NTFS 
作者:WebCrazy 
WebCrazy(tsu00@263.net) 

    NTFS是Windows NT引入的新型文件系统,它具有许多新特性。本文旨在探索NTFS的底层结构,所叙述的也仅是文件在NTFS卷上的分布。NTFS中,卷中所有存放的数据均在一个叫$MFT的文件中,叫主文件表(Master File Table)。而$MFT则由文件记录(File Record)数组构成。File Record的大小一般是固定的,通常情况下均为1KB,这个概念相当于Linux中的inode。File Record在$MFT文件中物理上是连续的,且从0开始编号。$MFT仅供File System本身组织、架构文件系统使用,这在NTFS中称为元数据(Metadata)。以下列出Windows 2000 Release出的NTFS的元数据文件(我将要给出的示例代码的部分输出结果)。 
    File Record(inode) FileName 
    ------------------ -------- 
          0             $MFT 
          1             $MFTMirr 
          2             $LogFile 
          3             $Volume 
          4             $AttrDef 
          5             . 
          6             $Bitmap 
          7             $Boot 
          8             $BadClus 
          9             $Secure 
         10             $UpCase 
         11             $Extend 

    Windows 2000中不能使用dir命令(甚至加上/ah参数)像普通文件一样列出这些元数据文件。实际上File System Driver(ntfs.sys)维护了一个系统变量NtfsProtectSystemFiles用于隐藏这些元数据。默认情况下,这个变量被设为TRUE,所以使用dir /ah将得不到任何文件。知道这个行为后使用i386kd修改NtfsProtectSystemFiles后即可以列出元数据文件: 

    kd> x ntfs!NtfsProtect* 
    fe213498  Ntfs!NtfsProtectSystemFiles 
    fe21349c  Ntfs!NtfsProtectSystemAttributes 
    kd> dd ntfs!NtfsProtectSystemFiles l 2 
    fe213498  00000001 00000001 
    kd> ed ntfs!NtfsProtectSystemFiles 0 
    kd> dd ntfs!NtfsProtectSystemFiles l 2 
    fe213498  00000000 00000001 
    kd> 

    D:\>ver 

    Microsoft Windows 2000 [Version 5.00.2195] 

    D:\>dir /ah $* 
     驱动器 D 中的卷是 W2KNTFS 
     卷的序列号是 E831-9D04 

     D:\ 的目录 

    2000-04-27  19:31               36,000 $AttrDef 
    2000-04-27  19:31                    0 $BadClus 
    2000-04-27  19:31               67,336 $Bitmap 
    2000-04-27  19:31                8,192 $Boot 
    2000-04-27  19:31                 $Extend 
    2000-04-27  19:31           13,139,968 $LogFile 
    2000-04-27  19:31           27,575,296 $MFT 
    2000-04-27  19:31                4,096 $MFTMirr 
    2000-04-27  19:31              131,072 $UpCase 
    2000-04-27  19:31                    0 $Volume 
                   9 个文件     40,961,960 字节 
                   1 个目录     51,863,552 可用字节 

    需要指出的是ntfs.sys将元数据文件以一种特殊的方式打开,所以在打开NtfsProtectSystemFiles后,如果使用ReadFile等产生IRP_MJ_READ等IRP包时将会导致Page Fault(详见Gary Nebbett的《Windows NT/2000 Native API Reference》)。 

    以上的讨论均是基于$MFT文件而讨论的,即基于$MFT中的File Record(inode)讨论的。为更好的继续以下的讨论,这儿我列出File Record Header的结构: 

    typedef struct { 
        ULONG Type; 
        USHORT UsaOffset; 
        USHORT UsaCount; 
        USN Usn; 
    } NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER; 

    typedef struct { 
        NTFS_RECORD_HEADER Ntfs; 
        USHORT SequenceNumber; 
        USHORT LinkCount; 
        USHORT AttributesOffset; 
        USHORT Flags;               // 0x0001 = InUse, 0x0002 = Directory 
        ULONG BytesInUse; 
        ULONG BytesAllocated; 
        ULONGLONG BaseFileRecord; 
        USHORT NextAttributeNumber; 
    } FILE_RECORD_HEADER, *PFILE_RECORD_HEADER; 

    下面我将讨论如何定位$MFT。稍微有点操作系统知识的人都会知道引导扇区(Boot Sector),其物理位置为卷中的第一个扇区。以下由dskprobe.exe(Windows 2000 Resource Kit中的一个小工具)分析的第一个扇区(当然也可以使用WinHex等其他应用程序): 

    File: d:\Sector00.bin 
    Size: 0x00000200 (512) 

    Address  | 00 01 02 03-04 05 06 07 : 08 09 0A 0B-0C 0D 0E 0F | 0123456789ABCDEF 
    ---------|-------------------------:-------------------------|----------------- 
    00000000 | EB 52 90 4E-54 46 53 20 : 20 20 20 00-02 08 00 00 | ?R?NTFS    ..... 
    00000010 | 00 00 00 00-00 F8 00 00 : 3F 00 F0 00-3F 00 00 00 | .....?..?.e.?... 
    00000020 | 00 00 00 00-80 00 80 00 : 90 C0 41 00-00 00 00 00 | ....
阅读(2124) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-09-28 15:48:35

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com