分类: 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 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 | .... 给主人留下些什么吧!~~
chinaunix网友2010-09-28 15:48:35 很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com |