Sunday, August 26, 2007

PDB Stream Decomposition

I've been taking a look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure of PDB files recently. PDB files are Microsoft's proprietary file format for storing debug information. They are generated by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Visual Studio family of products, and Microsoft has been kind enough to provide a full set of PDB files for its own operating system files since Windows 2000, which means that we get access to a whole bunch of great information like function symbols and type information. These can provide an excellent insight into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internals of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows operating system.

Naturally, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file format is undocumented and proprietary (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re used to be a page on MSDN stating as much, but I can't seem to find it now). However, Sven Schreiber, in his book Undocumented Windows 2000 Secrets: A Programmer's Cookbook, provides details on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 format and some sample code for reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information in such files. Although cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original code only deals with PDB files created with Visual Studio 6.0 and below, he has recently released a new program that parses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most recent version of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file format. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 newer format has not been written about, I thought I'd devote this entry to describing its high-level structure, and provide some code for breaking it down into its component parts. Hopefully I'll be following it up next week with some more polished code to parse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internal format of several of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 streams (such as stream 2, which contains type information), but as I'm going to be moving this week, it may get pushed off until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 week after.

(By cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way, in case you're wondering why I don't simply use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Debug Interface Access SDK to get at this information, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main reason is that it's not available for my preferred operating systems, Linux and OS X. I also think it's good in general to know what's really going on inside a file format; it gives you a better feel for how reliable cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information presented is (as opposed to just believing what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tool tells you), and can really save you when you find that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tool you use has some limitations or doesn't do what you expect.)

PDB files start with a fairly long, 0x20 byte signature:
'Microsoft C/C++ MSF 7.00\r\n\x1ADS\0\0\0'

Next come several dwords that give vital information and allow some basic integrity checking (note: I'm using Schrieber's names here):
  • dPageBytes - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of a page in bytes.
  • dFlagPage - page number of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 table describing which pages are allocated in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. If page n of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PDB file is in use, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 nth bit of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page will be set to 0.
  • dFilePages - number of pages in this file. dFilePages * dPageBytes should equal cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file's size.
  • dRootBytes - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream, in bytes.
  • dReserved - always zero.
  • adIndexPages[] - an array of dwords giving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pages numbers that contain cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream index. In fact, I do not have any evidence that this is an array in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest PDB format, racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than a simple dword, but Schreiber lists it as such.
All data in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PDB file is stored in blocks of a fixed size given in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dPageBytes member of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 header. As a result, most offsets in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file are given as a page number, racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than a byte offset. To get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual file offset, simply multiply cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page number by dPageBytes. Overlaid on top of this block structure, information is stored in streams, much like files on a filesystem. A single structure, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream, provides an index of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 streams, and on what pages cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y may be found. If this structure sounds familiar, that's because it appears to be quite a common one within Microsoft–this general layout is shared with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FAT filesystem, OLE (e.g. Word) documents, and Windows registry files.

If we seek to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page referred to by adIndexPages, we find that it contains an array of dwords giving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page numbers that make up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream. However, it is not immediately obvious how we are to know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of this array. In fact, we must calculate it: since we know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream from dRootBytes, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of a page from dPageBytes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of pages required to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream is given by dRootBytes / dPageBytes (round up). For example, in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PDB file for ntoskrnl.exe under Windows XP SP2, dRootBytes is 15964, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page size is 0x400 bytes, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of pages (and hence cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root stream index array) is 16. We can now read each page listed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 array to reconstruct cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full root stream.

The root stream itself has a fairly simple structure. It starts with a dword, dStreams, giving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of streams present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. Next is an array of dwords of length dStreams, giving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of each stream in bytes. Finally, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are dStreams arrays of dwords listing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pages that make up each stream. In ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure looks like:

[dStreams][length of stream 0][length of stream 1]...[length of stream n][first page of stream 0][second page of stream 0]...[last page of stream 0][first page of stream 1][second page of stream 1]

...and so on.

Once again, we have to calculate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of each array that lists cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page numbers for each stream. Luckily, it works exactly as before: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 length of each array is given by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 size of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stream divided by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page size, round up. Now that we have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list of pages that make up each stream, it is a simple matter to write out each stream separately.

Code to do just this can be found here (note: this may go down for a few days next week, as I move things to my new apartment. If anyone knows of a good shared host so I can stop hosting things off my desktop, let me know!) The usual caveats apply: use at your own risk, alpha quality code, and no support for older PDB files (some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XPSP2 debug symbols use this format; it looks like all of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Vista symbols use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new format, however).

That's all for now! Next time, I'm hoping to present cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginnings of a full-fledged PDB parser, including support for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internal structures of some streams, such as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type information (stream 2) and function symbols (stream 3). Personally, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 type information is most interesting to me; extracting this data from ntoskrnl.exe (ntkrnlmp.exe on Vista) allows us access to details of a number of internal kernel structures, which we can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n use to interpret cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of volatile memory. I used this technique with excellent results in my investigation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VAD tree. Eventually, I hope to be able to output information on Windows kernel structures in a format that can be inserted into Volatility's vtypes.py.