November, 2004

  • The Old New Thing

    Poking at diploma mills: Kennedy-Western University


    I enjoy poking around diploma mills. Especially the ones that spam my inbox. Like Kennedy-Western University, which describes itself like so:

    Since 1984 Kennedy-Western University (KWU) has provided distance and online degree programs to over 30,000 students. KWU is one of the largest non-accredited online universities in the United States. ...

    Ah, the magic word: "non-accredited". Translation: "Bogus".

    But hey, being non-accredited can't be all bad, right? After all, KWU seems to be proud of the fact that it isn't accredited.

    Read on:

    Three of four of its faculty of 140 credentialed professors - who are simultaneously employed by major traditional universities throughout the country - hold Ph.D. degrees from accredited universities.

    Oops, they're undermining their own statement. Isn't it kind of suspicious that they are bragging that their faculty is so good, they got their degrees from real universities (unlike this one)?

    My personal favorite diploma mill is Harrington University. It's fun kicking off a Google search to see how many people put a degree from that institution on their résumé.

    If you scroll down a bit on this Swedish web page, you'll find a picture of the building that houses so-called Brentwick University. The photo caption reads,

    Brentwick University claims to be on 196 High Street in northern London. This is what it looks like. On the second floor, over the dry cleaner's, lies Cordon Bell Accomodation Agency, a firm which serves as a maildrop and forwards mail on behalf of a series of camera-shy companies, among them "Brentwick University".

    It's a somewhat regular scandal in the United States that somebody well-known will be exposed to have been claiming a degree that was obtained by dubious means.

  • The Old New Thing

    How do I break an integer into its component bytes?


    Warning: .NET content ahead. For some reason, this gets asked a lot.

    To break an integer into its component bytes, you can use the BitConverter.GetBytes method:

    int i = 123456;
    byte[] bytes = BitConverter.GetBytes(i);

    After this code fragment, the byte array contains { 0x40, 0xE2, 0x01, 0x00 }.

    Update 11am: The endian-ness of the result is determined by the BitConverter.IsLittleEndian property. Thanks to reader Sean McVey for pointing this out.

  • The Old New Thing

    Exploiting the inattentive


    The makers of a certain major brand of detergent which I will not name (but which for the purposes of this discussion will be called "Snide") appears to take every step to exploit inattentive customers.

    A box of Snide detergent powder comes with instructions indicating that for a normal-sized load, you should use 3/8 cup of detergent; for a large load, 1/2 cup.

    The detergent box also comes with a handy measuring cup.

    The measuring cup holds 5/8 cup of detergent.

    Not to be outdone, Liquid Snide plays a similar trick.

    The instructions indicate that for a normal-sized load, you should fill the cup to line 1 (the lowest line). For a large load, fill to line 2. If you look at the cup they provide, there is also a line 3 which is even higher than lines 1 and 2. Not even counting the imaginary "line 4" which is what you get if you fill the cup to the brim.

    Just because it comes with a measuring cup doesn't mean you have to use it.

  • The Old New Thing

    What is this Xerox directory doing in Program Files?


    If you go snooping around, you may find an empty C:\Program Files\Xerox directory. What's that for?

    This directory is being watched by Windows File Protection, because it needs to protect the file xrxflnch.exe should it ever show up. (Why does the directory have to exist in order for Windows File Protection to be able to watch it? I'm told it's a limitation of the Windows File Protection engine. I suspect it may have something to do with the fact that the FindFirstChangeNotification function can't watch a directory that doesn't exist.)

    Why is xrxflnch.exe so special? I don't know. My guess is that it's some file that is frequently overwritten by setup programs and therefore needs to be protected.

  • The Old New Thing

    Asking questions where the answer is unreliable anyway


    Here are some questions and then explanations why you can't do anything meaningful with the answer anyway even if you could get an answer in the first place.

    "How can I find out how many outstanding references there are to a shared memory object?"
    Even if there were a way to find out, the answer you get would be instantly wrong anyway because the microsecond after you ask the question, somebody can open a new handle.

    This is an example of "Meaningless due to unavoidable race condition."

    "How can I find out whether a critical section is free without entering it?"
    Again, once you get an answer, the answer could instantly become wrong if another thread decides to enter the critical section immediately after you checked that it was free.

    "How can I tell whether there is a keyboard hook installed in the system?"
    This suffers from the same problem yet again: The instant you get the answer ("all clear"), somebody can install a hook.

    This is actually even worse because people who ask this question are typically interested in secure keyboard access. But if somebody has a keyboard hook installed, that means that they have already injected code into your process (namely, the hook itself). At which point they could easily patch the imaginary IsKeyboardHooked() function to always return FALSE.

    Now when your program asks if the keyboard is hooked, the answer is a happy "no" and you proceed, blithely confident that there are no hooks. Just because somebody said so.

    You cannot reliably reason about the security of a system from within the system itself. It's like trying to prove to yourself that you aren't insane.

    The system may itself have already been compromised and all your reasoning therefore can be virtualized away. Besides, your program could be running inside a virtual PC environment, in which case the absence of a keyboard hook inside the virtual PC proves nothing. The keyboard logging could be happening in the virtual PC host software.

    From a UI standpoint, the desktop is the security boundary. Once you let somebody run on your desktop, you implicitly trust them. Because now they can send your program random messages, inject hooks, hack at your window handles, edit your menus, and generally party all over you.

    That's why it is such a horrible mistake to let a service interact with the desktop. By joining the interactive desktop, you have granted trust to a security context you should not be trusting. Sure, it lets you manipulate objects on that desktop, but it also lets the objects on that desktop manipulate you. (There's a Yakov Smirnoff joke in there somewhere, but instead I will quote Nietzsche: Wenn du lange in einen Abgrund blickst, blickt der Abgrund auch in dich hinein.)

    If you're a service, you don't want to start letting untrusted programs manipulate you. That opens you up to a Shatter attack.

  • The Old New Thing

    Will dragging a file result in a move or a copy?


    Some people are confused by the seemingly random behavior when you drag a file. Do you get a move or a copy?

    And you're right to be confused because it's not obvious until you learn the secret. Mind you, this secret hasn't changed since 1989, but an old secret is still a secret just the same. (Worse: An old secret is a compatibility constraint.)

    • If Ctrl+Shift are held down, then the operation creates a shortcut.
    • If Shift is held down, then the operation is a move.
    • If Ctrl is held down, then the operation is a copy.
    • If no modifiers are held down and the source and destination are on the same drive, then the operation is a move.
    • If no modifiers are held down and the source and destination are on different drives, then the operation is a copy.

    This is one of the few places where the fact that there are things called "drives" makes itself known to the end user in a significant way.

  • The Old New Thing

    Advantages of knowing your x86 machine code


    Next time you find yourself debugging in assembly language (which for some of us is the only way we debug), here are some machine code tricks you may wish to try out:

    This is the single-byte NOP opcode. If you want to patch out code and don't want to think about it, just whack some 90's over it. To undo it, you have to patch the original code bytes back in, of course.

    This is the single-byte INT 3 opcode, which breaks into the debugger.

    These are the opcodes for JZ and JNZ. If you want to reverse the sense of a test, you can swap one for the other. Other useful pairs are 72/73 (JB/JNB), 76/77 (JBE/JA), 7C/7D (JL/JGE), and 7E/7F (JLE/JG). You don't have to memorize any of these values; all you have to recognize is that toggling the bottom bit reverses the sense of the test. To undo this, just flip the bit a second time.

    This is the unconditional short jump instruction. If you want to convert a conditional jump to an unconditional one, change the 74 (say) to EB. To undo this, you have to remember what the original byte was.

    On the other hand, if you want to convert a conditional short jump to a never-taken jump, you can patch the second byte to zero. For example, "74 1C" becomes "74 00". The jump is still there; it just jumps to the next instruction and therefore has no effect. To undo this, you have to remember the original jump offset.

    These are the opcodes for the "MOV EAX,immed32" and the "CALL" instructions. I use them to patch out calls. If there's a call to a function that I don't like, instead of wiping it all to 90's, I just change the E8 to a B8. To undo it, change the B8 back to an E8.

    It has been pointed out that this works only for functions that take zero stack parameters; otherwise, your stack gets corrupted. More generally, you can use 83 C4 XX 90 90 (ADD ESP, XX; NOP; NOP) where XX is the number of bytes you need to pop. Personally, I don't remember the machine code for these instructions so I tend to rewrite the CALL instruction so it calls the "RETD" at the end of the function.

    I prefer these single-byte patches to wholesale erasure with 90 because they are easier to undo if you realize that you want to restore the code to the way it was before you messed with it.

  • The Old New Thing

    Why does Windows not recognize my USB device as the same device if I plug it into a different port?


    You may have noticed that if you take a USB device and plug it into your computer, Windows recognizes it and configures it. Then if you unplug it and replug it into a different USB port, Windows gets a bout of amnesia and thinks that it's a completely different device instead of using the settings that applied when you plugged it in last time. Why is that?

    The USB device people explained that this happens when the device lacks a USB serial number.

    Serial numbers are optional on USB devices. If the device has one, then Windows recognizes the device no matter which USB port you plug it into. But if it doesn't have a serial number, then Windows treats each appearance on a different USB port as if it were a new device.

    (I remember that one major manufacturer of USB devices didn't quite understand how serial numbers worked. They gave all of their devices serial numbers, that's great, but they all got the same serial number. Exciting things happened if you plugged two of their devices into a computer at the same time.)

    But why does Windows treat it as a different device if it lacks a serial number and shows up on a different port? Why can't it just say, "Oh, there you are, over there on another port."

    Because that creates random behavior once you plug in two such devices. Depending on the order in which the devices get enumerated by Plug and Play, the two sets of settings would get assigned seemingly randomly at each boot. Today the settings match up one way, but tomorrow when the devices are enumerated in the other order, the settings are swapped. (You get similarly baffling behavior if you plug in the devices in different order.)

    In other words: Things suck because (1) things were already in bad shape—this would not have been a problem if the device had a proper serial number—and (2) once you're in this bad state, the alternative sucks more. The USB stack is just trying to make the best of a bad situation without making it any worse.

  • The Old New Thing

    A history of GlobalLock, part 4: A peek at the implementation


    On one of our internal discussion mailing lists, someone posted the following question:

    We have some code that was using DragQueryFile to extract file paths. The prototype for DragQueryFile appears as follows:

    UINT DragQueryFile(
        HDROP hDrop,
        UINT iFile,
        LPTSTR lpszFile,
        UINT cch

    In the code we have, instead of passing an HDROP as the first parameter, we were passing in a pointer to a DROPFILES structure. This code was working fine for the last few months until some protocol changes we made in packet layouts over the weekend.

    I know that the bug is that we should be passing an HDROP handle instead of a pointer, but I am just curious as to why this worked so flawlessly until now. In other words, what determines the validity of a handle and how come a pointer can sometimes be used instead of a handle?

    GlobalLock accepts HGLOBALs that refer to either GMEM_MOVEABLE or GMEM_FIXED memory. The rule for Win32 is that for fixed memory, the HGLOBAL is itself a pointer to the memory, whereas for moveable memory, the HGLOBAL is a handle that needs to be converted to a pointer.

    GlobalAlloc works closely with GlobalLock so that GlobalLock can be fast. If the memory happens to be aligned just right and pass some other tests, GlobalLock says "Woo-hoo, this is a handle to a GMEM_FIXED block of memory, so I should just return the pointer back."

    The packet layout changes probably altered the alignment, which in turn caused GlobalLock no longer to recognize (mistakenly) the invalid parameter as a GMEM_FIXED handle. It then went down other parts of the validation path and realized that the handle wasn't valid at all.

    This is not, of course, granting permission to pass bogus pointers to GlobalLock; I'm just explaining why the problem kicked up all of a sudden even though it has always been there.

    With that lead-in, what's the real story behind GMEM_MOVEABLE in Win32?

    GMEM_MOVEABLE memory allocates a "handle". This handle can be converted to memory via GlobalLock. You can call GlobalReAlloc() on an unlocked GMEM_MOVEABLE block (or a locked GMEM_MOVEABLE block when you pass the GMEM_MOVEABLE flag to GlobalReAlloc which means "move it even if it's locked") and the memory will move, but the handle will continue to refer to it. You have to re-lock the handle to get the new address it got moved to.

    GMEM_MOVEABLE is largely unnecessary; it provides additional functionality that most people have no use for. Most people don't mind when Realloc hands back a different value from the original. GMEM_MOVEABLE is primarily for the case where you hand out a memory handle, and then you decide to realloc it behind the handle's back. If you use GMEM_MOVEABLE, the handle remains valid even though the memory it refers to has moved.

    This may sound like a neat feature, but in practice it's much more trouble than it's worth. If you decide to use moveable memory, you have to lock it before accessing it, then unlock it when done. All this lock/unlock overhead becomes a real pain, since you can't use pointers any more. You have to use handles and convert them to pointers right before you use them. (This also means no pointers into the middle of a moveable object.)

    Consequently, moveable memory is useless in practice.

    Note, however, that GMEM_MOVEABLE still lingers on in various places for compatibility reasons. For example, clipboard data must be allocated as moveable. If you break this rule, some programs will crash because they made undocumented assumptions about how the heap manager internally manages handles to moveable memory blocks instead of calling GlobalLock to convert the handle to a pointer.

    A very common error is forgetting to lock global handles before using them. If you forget and instead just cast a moveable memory handle to a pointer, you will get strange results (and will likely corrupt the heap). Specifically, global handles passed via the hGlobal member of the STGMEDIUM structure, returned via the GetClipboardData function, as well as lesser-known places like the hDevMode and hDevNames members of the PRINTDLG structure are all potentially moveable. What's scary is that if you make this mistake, you might actually get away with it for a long time (if the memory you're looking at happened to be allocated as GMEM_FIXED), and then suddenly one day it crashes because all of a sudden somebody gave you memory that was allocated as GMEM_MOVEABLE.

    Okay, that's enough about the legacy of the 16-bit memory manager for now. My head is starting to hurt...

  • The Old New Thing

    A history of GlobalLock, part 3: Transitioning to Win32


    Now that you know how the 16-bit memory manager handled the global heap, it's time to see how this got transitioned to the new 32-bit world.

    The GlobalAlloc function continued to emulate all its previous moveability rules, but the return value of GlobalAlloc was no longer a selector since Win32 used the processor in "flat mode".

    This means that the old trick of caching a selector and reallocating the memory out from under it no longer worked.

    Moveability semantics were preserved. Memory blocks still had a lock count, even though it didn't really accomplish anything since Win32 never compacted memory. (Recall that the purpose of the lock count was to prevent memory from moving during a compaction.)

    Moveable memory and locking could have been eliminated completely, if it weren't for the GlobalFlags function. This function returns several strange bits of information—now entirely irrelevant—the most troubling of which is the lock count. Consequently, the charade of locking must be maintained just in case there's some application that actually snoops at the lock count, or a program that expected the GlobalReAlloc function to fail on a locked block.

    Aside from that, moveable memory gets you nothing aside from overhead.

    The LocalAlloc function also carries the moveability overhead, but since local memory was never passed between DLLs in Win16, the local heap functions don't carry as much 16-bit compatibility overhead as the global heap functions. LocalAlloc is preferred over GlobalAlloc in Win32 for that reason. (Of course, many functions require a specific type of memory allocation, in which case you don't have any choice. The clipboard, for example, requires moveable global handles, and COM requires use of the task allocator.)

    Next time, an insight into how locking is implemented (even though it doesn't do anything).

Page 2 of 3 (27 items) 123