 All right. Welcome. This song is called Function Hooking for OSX and Linux. My name is Joe. Okay. So these slides are on my blog, timetobleed.com. You can go get them right now if you want to follow along. Instead of giving you guys a long intro about who I am and why I'm cool, I just want to give a shout out to SoftSec for getting my back door in the conference because this is my first DEF CON and I didn't know anybody. And free jump ESP. And I also want to say sorry to the mom and the kids that I elbowed in the face as I was running through Caesars to get to the Apple store for an adapter. My bad. All right. So I'm not a security researcher. I just have a t-shirt that says security. So you can feel free to get on Twitter and call me a script kitty. I'm just Joe DiMotto. All right. So here's the problem. I was running around doing Ruby consulting for a lot of shops and I noticed the common problem amongst everybody was that they're Ruby binaries after running in memory for a long time. Kind of look like that. They're very large and slow. And people want the Ruby binaries to be small, thin and fast. But the problem is that most people are really lazy. They don't want to install custom patches or rebuild the Ruby binary or do anything too complicated. They just want to install some library that will allow them to figure out where the Ruby binary is leaking memory and adjust the Ruby code and then live happily ever after. So it turns out that in order to do that, there's a set of functional techniques you can employ to make this all possible. So that's what this talk is about. This talk is about three functional techniques that I researched and implemented to build a Ruby memory profiler. But since I know there's people in the audience that don't really give a shit about performance analysis and memory profilers, there's also an evil example at the end of what these things can be applied to. So just a quick note before we get rolling, there's a lot of assembly syntax in this. There's a lot of assembly code in this talk. And it'll all be an AT&T syntax. So I can't, sorry, I saw somebody say, oh, I can't read anything other than AT&T, my bad. So to actually do this, we need to know a few things, right? So we need to know about the ABI for the system we're working on. The question we need to start with though is what the fuck's an ABI? An ABI is an application binary interface. So what does an ABI actually tell us? It tells us a few things. Some of the more important things for this talk are it tells us about alignment, the alignment of the program stack, data types, stuff like that. It tells us about calling convention, how functions are called, where arguments live, how that stuff works, and about object file and library formats. The great thing about the ABI's is that they form a hierarchy, kind of like this hierarchy of beards. These beards all are related in some way, they're all parts of each other, and the ABI's are the exact same way. You have the big system 5 ABI, which is like 200 pages, and then as you read it, it has sections where it says like, hey, at this point, open up your architecture-specific ABI and read that one. And the great thing about that is that the AMD64 ABI has a page in it where it says, hey, everything in here is true, but you also need to read everything in the I3D6 ABI. So if you actually want to understand what's going on, you need to read like 700 pages. And there's also ABI's for MIPS, ARM, PBC, and itanium as well. But lucky for us, the x8664 calling convention on OSX is based on the system 5 AMD64 ABI. So there's some overlap, so we actually get saved a little bit. So instead of actually reading all this stuff, we can just distill it down into sort of the main things we need to actually accomplish the function hooking that I'm going to describe shortly. So the first thing we need to worry about is alignment. And all that we really care about for alignment in this talk with function hooking is that the end of the argument area before you call a function has to be aligned on a 16-byte boundary. So the way this is usually accomplished is either the compiler will generate code that never misaligns the stack or you'll see instructions like this sprinkled throughout your code that are just aligning the stack pointer. We also need to care about calling convention. So for calling convention, all we really care about is that function arguments, wow, that color is horrible, function arguments live in registers left to right. So first arguments in RDI, seconds in RSI, et cetera, et cetera. But that's only for integer class items. Other stuff gets passed on the stack similar to the way arguments get passed on I-36 if there's any I-36 hackers in the room. Registers are either caller or quality saved. And that's all we pretty much need to know about calling convention to make binary patching work later. The big thing we need to know more about though is object file and library formats. And we care about two of them. The one we care about is ELF, the executable linkable format. This is the format that was chosen as the standard format in the system 5 ABI for, oh, really? Oh, hey, dad. Sorry, let me turn that off real quick. All right. So the ELF ABI was picked as the standard for UNIX and UNIX like operating systems. So what does that look like? Well, it kind of looks like this. You have an ELF header, you have a program header table, various sections, a text section, a data section, and then you have another table at the bottom. So the program header table indexes different segments. So segments contain sections. And the section header table at the bottom just indexes the sections. You can use a library like LibElf to help you wander through an ELF object extracting useful information. The thing to keep in mind is that executables and shared objects each have their own set of data. So the picture that we were just looking at, this guy right here, this set of sections and data exist for each library, your executable loads. And that will be important later and you'll see why. So a couple sections that are important to know about. You have the text section where code lives. You have the PLT section. This is stub code that helps to resolve absolute function addresses. The got PLT, this is where absolute function addresses are stored and these are used by the PLT entries. There's also a couple debugging sections, debug info and GNU debug link. There's two other pretty useful sections. You got the dinsim and the dinsster. These two sections are used for dynamic linking. So dinsim creates a mapping between exported symbol names and offsets and dinsster stores the actual symbol names themselves. And in addition to those, there's two other sections, the sim tab and the stir tab. Sim tab and stir tab are actually super sets of the information in the dinsim and the dinsster. They include other things like local variables and other symbols that aren't exported. This is useful for debugging. Like if you get a back trace and you want to know what's going on, what arguments the function takes, you'll need stuff from the stir tab and the sim tab. And there's lots of other sections like for stack unwinding and exception handling and stuff like that. Okay, so the other binary format we care about is the mock o binary format. This is the binary format used on OSX. Here's a picture of what it looks like. It starts with a header. It consists of a bunch of load commands. Load commands describe the way the binary is going to be laid out in memory. So there's a segment command. The segment command is a pretty important one. It describes how segments are going to be laid out. And much like with Elf, you have segments and segments contain sections. And we're going to go through some of the sections in a bit. Right, so the man page for DYLD, if you have a Mac box, you can check out the man page. And it has a bunch of APIs for touching mock o objects. So you can iterate through the objects that are mapped into the process. You can play around with them and do stuff. And it's pretty useful. And just like with Elf, each dilib or bundle that gets loaded has its own set of data in the picture that we saw before. So just a couple of sections that are sort of similar to Elf. We have the text section. This is where code lives. There's the symbol stub section. This is just a list of jump queue instructions that are useful for runtime dynamic linking. We're going to go over runtime dynamic linking in a bit, so don't worry. There's going to be a really pretty picture. That'll be hard to see, but if you're looking at the slides online, it'll make a lot more sense. So there's also the stub helper, stub code that helps to resolve absolute function table entries that are referenced by the stub helpers above. The other important thing to note is that symbol tables do not live in a segment. They have their own load command, the LC sim tab load command. So this is just basically a structure that tells you, hey, at this spot in memory there's a bunch of strings and there's a bunch of mappings from these strings to offsets. And there's also a die sim tab. So the die sim tab in Mako, which describes how dynamic functions work, that's actually just a list of offsets into the sim tab. So on Elf you have two separate tables. On Mako you have one table and then you have a set of indexes into that table for things that are exported. All right, so let's look at a couple tools, some useful tools for dissecting objects before we actually start writing codes. So you've got NM. NM's pretty useful. It comes on OSX and Linux. You run NM on your binary. You get symbol values on the left and symbol names on the right. And down the middle there's information about what type of symbol it is. You can get it on Linux. If you want it for OSX you have to go find binutils and build it. I have no idea why it doesn't come standard. So if you just run object dump on say your ruby binary you get offsets on the left, opcodes, instructions, and then helpful metadata. There's also read Elf. Read Elf is obviously for Elf objects. This comes on Linux. Output is a huge amount of information. This is just sort of a small screenshot of what you get. It's really, really useful. And O tool is useful information about Mako objects. We also need to talk about strip. Not that kind of strip. There's a strip binary that you can run. It'll strip out whatever sections you want. You can go through stripping as many sections as you want out of your binary. But the problem is your binary may not actually fucking work after you do that. So you need to obviously leave the text section in. If you strip the text section you have no codes and having no codes is no fun. You also need to leave in the dynamic file. Okay. So we know enough stuff. Let's start hooking some functions. So functions can be called in lots of different ways on x86. There's two really common ways. One common way is calling indirectly via a register. Like you see on the top. So in this example, rbx will just hold the address of the function you want to call and it'll just indirectly call the function. Or the way on the bottom, it looks like when you run object dump you'll see things like you see on the bottom it's not the actual full address. It's just a 32-bit displacement. So what does that actually mean? Well, we can do an anatomy of a call instruction and get a better idea of what's going on. So this is just object dump output. And as you see on the right there's a call instruction and it's calling the function A function and the address that it lives at is 4363 DC. So the way that's calculated is, so an object dump. This is the address of this instruction. This is the call instruction. And then the four bytes after it are the 32-bit displacement to the function you're trying to call. The way that's actually calculated is you take, we're trying to call A function so that's that address 4363 DC. That's calculated by taking the address of the next instruction that's going to be executed and adding it to the 32-bit displacement. Remember that x86 is a little endian so you need to flip the bytes around before you add it. So hooking A function is actually to replace the displacements that you see and replace the displacements to call your own handler function instead. And your handler function can do whatever you want. In my case, since I'm building a memory profiler, I actually want the Ruby binary to keep working after I'm done overwriting stuff. So I'm going to do my thing and I'm also going to call the original function so that the Ruby VM could do what it was supposed to do normally. Writing the codes for this are, the codes are easy, right? This is just a call instruction. You just do some math and you can check if the 32-bit displacement matches the function that you're trying to hook and if it does, you just overwrite that to call your handler function instead. Right? I mean, like how hard could it possibly be? Well, it turns out that it's actually a little more tricky than just that so you need to be kind of careful. When you're dealing with the 32-bit displacement, overwriting an existing call with another call, that's fine, right? Like the stack will already be aligned because we were going to call a function anyway for a different call so alignment's fine, the registers are all fine, everything's good to go, everybody's cool. But the problem is we can't redirect a code that's outside of the instruction pointer plus the 32-bit displacement because we can only put in a 32-bit displacement so if you're redirecting a code that's far away, you get fucked. The only way to get around this is to scan the address space searching for a free slot where you can M-map a page in to put in a bunch of code to call. So that's how you get around that. You basically call the instruction pointer plus 32-bit displacement window. And that works but it doesn't work for everything. So if you call a function that's exported by a dynamic library, that works slightly differently. So we need to talk a little bit about how runtime dynamic linking actually works. So I'm going to describe how it works on ELF, but it works almost identically on Mach O and I'm going to have a pretty picture soon that's going to show the difference between ELF and Mach O. The code is exactly the same, it's just the instructions are rearranged slightly. The function that has a function that's exported, so if you look at the top you can see over there on the far right it says RBNewObject at PLT. RBNewObject is just some function in the Ruby VM. And at PLT, that's object dump is telling us, hey, you're not actually calling that function specifically, you're calling this dynamic linking thing that's going to go find it and actually execute it later. So if we disassemble instructions starting at that address, you see there's three instructions at the top, there's a jump, a push, and a jump. The first jump is referring to an address that exists, that address lives in the got PLT section, that is actually an entry in a table. That entry initially points at the address of the push instruction. Okay, so I'm going to say that again because the first time I saw this I didn't understand why that was. So initially when you first execute your binary before this function is called for the first time, the initial state of the system is that you have a jump instruction that's jumping to an address stored in a table. The address stored in the table points at the push instruction that comes right after the jump. Okay, now the reason why that is is because after you execute that jump and you land right there on that push instruction, you execute the push instruction which puts an ID on the stack and then it invokes the runtime dynamic linker. Runtime dynamic linker goes and it runs around and it finds the function you wanted to call using the ID that was pushed. It fills in the address of the function in the table. That way subsequent calls to the same function will just land you in that function directly instead of landing you back at that push instruction. Did that make sense? Is everybody cool with that? All right, word. So hooking that is actually really easy. We can just pretend that we're the dynamic linker and go around poisoning other people's global offset table entries. So we just redirect execution by cruising around finding other people's PLT entries and then with the address that we care about. So how that would work, here's an example. You have this thing, you're calling RB new object. The table entry is poisoned with an address of a bad function. Let's just say the address is OX dead beef and you land in this other function where you can do whatever you want. But the question that might be running through your mind now, I hope it's running through your mind is, hey Joe, in your handler function, you're calling the function you're trying to hook. So shouldn't that be an infinite loop? It turns out that's not because if you remember from earlier, each dynamic object that you load into your process has its own set of data. So each dynamic object has its own set of PLT entries and its own global offset table. So as long as you poison everybody else's except for the got PLT section of the library that includes your bad code, as long as you don't poison your library itself then you'll be okay. Dynamic Lincoln will happen normally for you. You just have to overwrite everybody else's shit. So what about Mako? We just described how it works in Elf, but what about Mako? It's not quite like comparing apples to oranges. It's actually really similar. This is the graphic that I was talking about that's super sexy that I spent a lot of time drawing. Elf is on the top, obviously Mako is on the bottom. The actual assembly instructions that are Elf, you have a jump instruction followed by invoking the dynamic linker as we saw on the other slides. But on Mako, they just put all the jump instructions together and then all the dynamic linking invocation functions together. So they just rearrange stuff, but it's exactly the same thing, it works exactly the same way. Okay, so we don't have to hook two types of functions, but there's a third type that also matters, inline functions, and this is actually really fucking tricky. There's a function that I needed to hook free list, but the problem with trying to hook free list is that it's marked as inline, which means that the compiler has the option of inserting the instructions for this function directly into whoever is calling this function. So if that happens, you won't actually see any explicit call instructions in your binary. So what do we do now? Are we jacked or is there a way around this? Well, it turns out that if you look closely at this generated code you should notice something. You notice that there's this identifier called free list, and free list is getting updated to point to the new head of the free list as objects are recycled. Notice that free list isn't defined in this function, so it has file level scope. So what about a crazy, crazy stupid idea? Since free list has file level scope it lives at some static address. Add free list updates the free list by just inserting something at the head of the free list. Why not just search the binary for all move instructions that get free list and just overwrite all of those? And it turns out that that might actually work, but there are a few problems. The system isn't actually ready to make a call instruction at that point. What does that mean not ready? Well, remember before we talked about the ABI and how the ABI talks about alignment and calling convention? Well, it's not quite ready to call a function because there was just a bunch of move instructions. If we just implant a call instruction there's no guarantee that the stack will be ready to write registers. So what do we need to do? Well, it turns out that we can actually use another type of instruction instead. We can actually just use a jump. We can insert a jump instruction, transfer execution to an assembly stub that we generate at runtime, and then recreate the instruction we just overwrote, set the system up to call a function, and then do whatever we're going to do whether it's good or bad or whatever, and then jump back and resume sort of like the crusty third hook that you need to pull this off. So there's a checklist of things we need to do. We need to save and restore any registers. We need to align the stack. We need to regenerate the instruction that we're overwriting. We need to arrange for any arguments that we care about to end up being put in the right register so they get passed to the function. We need to invoke our code that does whatever it's going to do, and then we need to resume execution as if nothing happened. So here's an example of an instruction that is updating the free list that we're going to overwrite. So this is just saying hey, you know, I'm going to take RBX and I'm going to write it to the top of the free list. So we can't put a call instruction there because as we said, the system is not ready to make a function call. The stack isn't aligned, arguments aren't in the right place, everything's all fucked up. So what we can do instead is we can just insert a jump instruction. So it turns out that the move instruction is seven bytes. The jump instruction, including the offset, is just to make everything work out. So this address right there, that's just the address of the assembly stub we're going to transfer execution to. And when the assembly stub finishes, we want to return back to this knob and then just fall through and continue executing as if nothing happened. So here's a shortened assembly stub that actually does this. This is an assembly stub that, when I ran my code, this was generated at runtime. It has to be generated at runtime because this first instruction up here at the top, this is getting overwritten and it's a memory, right? Like everybody compiles around Ruby. And so this could, instead of using RBX, who knows, maybe you're using RCX on your Ruby binary. So you just search for anything that targets a free list, you overwrite it with a jump and then you have to regenerate what you overwrote on this little trampoline here. This is actually shortened. The real thing is way, way longer and way uglier. But I'm going to provide a link to the code later. That way you can actually check it out if you want to see what the deal is. So yeah, we need to save RDI. That's where the first argument lives if you remember from earlier in the talk. We're going to move the free list into RDI because for a memory profiler I want to know who just got freed. That way I can track information about them. I'm going to save RBX because I'm going to fill it with a value in a minute. I'll align the stack to comply with the ABI. And then I'll move the address of a handler function into RBX. That's why I saved RBX a few instructions earlier. I'll call my handler function indirectly via RBX. And then the handler function will just do something good or something bad wherever you want to do. You do a bunch of cleanup and then you just jump back to wherever you overwrote the instruction originally. And the crazy thing about all this hackery is that when you're done it actually works. You can actually use this. If you have Ruby and you care you can gem install memproff and you can actually use this thing. And all the code that hooks these three things together with memproff. You can go on GitHub, you can read the code, and it's real. And it's actually pretty powerful. These three things can be used to build lots and lots of really interesting tools. So just to give you a quick rundown of some of the cool things you can do with this. So we can do stuff about memory. I can write this Ruby code. I can say hey include my memory profiler, start profiling, run a bunch of code and give me stats. And I'll get stats I can look and I can say hey, on file 1.rb online 25 I'm creating a million objects. So I can know exactly where things are leaking which is pretty fucking sick. So the tech space output is cool but it's really painful to grok. So my roommate Amon built a web UI for this thing so you can actually traverse stuff and you can learn all kinds of crazy information. You can find who's holding references to what other objects where you're leaking memory. You can look at the sub classes of string in rails. You can explore the socket hierarchy. You can do all kinds of stuff. You can find massive arrays. And since in Ruby all Ruby code is just stored in the heap, I can go and regenerate Ruby code from using these function hooks. I can just grab these things and they're getting created and I can reverse them back into Ruby code which is pretty cool too. You can go and look through Ruby standard library and you can say if you have sockets open to any web services you can see what you're connected to on what port. And that's all pretty cool. There's also performance information too. You can find out how long requests take, which rails control you hit, request information. You get crazy stuff too. You can track my SQL queries. Since I can hook into anything, I can track anything I want. I can track my SQL queries, how many I make, how long they take. I can track the garbage collector, how many calls there are to the GC, how long they take. I can track how many objects are created in general which is pretty useful as well. And that's all cool but what about some evil things we can do instead? The performance stuff is cool but maybe you don't care about performance. You can build a library that is something evil but you'll need a social engineering aspect of it because you need some way to get people to use your library. In a few slides we'll talk about how to get around this so you don't need to social engineering. How would we do that? Well, evil lives. I built an evil version of Memproff. The branch on GitHub is just dnw, the sensor do not want. If you try to build it, it will fail. I put a bug in the make file that way nobody can accidentally go screw themselves by building this. The way I would social engineer somebody to use this is I would say, hey bro, this makes Ruby faster. It does make Ruby faster because what it does is it hooks the read system for a magic cookie. The magic cookie is my name, Joe. It disables the garbage collector. The result is Ruby is fast. But it is now a time bomb, right? Eventually it will eat all the RAM on your system and your system will melt down. If you really want to drill it in, hey, it makes Ruby faster. Here's a bullshit benchmark but don't tell them it's bullshit. Here's 7.5 seconds to do the stupid benchmark. 26 requests per second. After you use this thing that makes Ruby faster, you shave off a second off the time and you're now up to 30 requests per second. You show this to a Ruby developer and they're like, dude, I need this right now. Anyway, this example is stupid but the whole point is the proof of concept. You can do anything. You could hook, read, and write and phone home with all the data and see a specific cookie. You can do whatever you want. But the shitty thing is that we have to social engineer and that's kind of a problem. This guy also has a problem. Unrelated to social engineering. So the problem with social engineering, at least from my perspective, is that it's painful and I don't want to have to social engineer things if I don't have to, like that. So it would be cool if there was a way to inject these evil libraries into running processes without having to give in people to download my thing and look at some bullshit benchmark. It turns out that there is a way of doing this. There is a project written by this guy called Sean Close or Clouds or something. It's called InjectSO. It injects libraries into running processes using ptrace. This hack is actually super, super clever to borrow a very awesome InjectSO works because it's pretty cool. It's actually pretty simple. It just uses ptrace. Ptrace allows you to view and modify the register set in address space of another process. Any permissions that are set on the memory in that process are ignored. So even if a page is marked as read only, you can still write all over it or do whatever you want. So the way InjectSO works is it just attaches to a target process using ptrace. It saves a copy of a small piece of the program stack. It saves a copy of the register set to save return address of zero. You then set the register set to point DL open. So you have the instruction pointer pointing to the address of DL open. You set up the argument. So you have the path to the shared object, the mode stored in RSI. And then you just let it rip. So you wait and then eventually it'll seg fault because once DL opens done, it'll load in this library that you want it to load in and then it'll seg fault because the save return address is zero. When the seg fault happens, we'll get a signal. So this child process will get suspended. You'll be awoken and you'll be allowed to do stuff to it. So you just restore the stack, restore the register set, and just let the world continue on its merry way as if nothing bad happened. This actually works. It's pretty cool. But there's a couple downsides. There's downsides to both aspects of the ptrace method and the evil DSO method. Remote allocating memory is a pain in the ass. It's doable, but it's just really painful to do. Seg faults and running processes might be bad. Perhaps the system administrator has some alert thing set up so that if a seg fault happens, then they get an email. I have that set up on my boxes, so if I get a seg fault, I know that something bad happened. And binary patching in general is hard to do. The stuff that we just talked about earlier, the three types of functional king, those things are complicated. And it's possible to do with ptrace, but adding another layer of difficult in between you and actually doing what you want to do. So binary patching is hard, but doing it with ptrace just makes it harder. The problem with the evil DSO though is getting the user to use your library might be hard. And already running processes will need to be killed and restarted, which also sucks. And you need to poison the app each time it started. And in general, binary patching is hard. But what if there was a way to bootstrap the process? What if we can just combine both methods? We can just use inject SO to load the evil DSO and take it from there and do all the crazy binary patching stuff. So in order to do that, we needed a 64-bit version of inject SO. But it turns out that the inject SO that I talked about by that dude Sean is only 32-bit. So I was getting ready to write a 64-bit port and I searched the internet a bit and I found this guy stealth ported inject SO to 64-bit boxes. This is his blog post releasing the source code. I did a bunch of trivial cleanup on it and it was suffering from some bit rot. So I did some minor cleanup and I put the code on the githubs. So if you want to copy that, you can go there and check it out. I tested it on a 64-bit Ubuntu VM and it actually works. So I can use inject SO and I can inject my stupid thing that looks for the string Joe and disables the garbage collector and then it DOSes my box. So it's pretty cool because like I had this thing, I had this web server running and I just used it. I was going to try to demo that but then I wouldn't be able to give the rest of the talk. So I can demo that. So anyway, using those two things, I think it's kind of cool. I don't know if it's a jackpot or not but it's definitely pretty sweet. So we talked about some attack mechanisms. So what are some defense mechanisms to protect against this sort of thing? Like I said earlier, I'm not a security researcher so I don't know all the up-to-date amazing defense mechanisms that are out there. But between defense mechanisms that are difficult to implement, difficult to understand versus just like, you know, standard run-of-the-mill like Ubuntu out of the box, right? So I went through some of the defense mechanisms that I know about and discussed like really briefly like why they're good or why they're bad or whatever. So I'm sure you guys know about the NX bit and no XQ bit. That's cool but if we just load our DSO and our DSO can just call and protect and just say, hey, this page that was about debug information but most people just use pre-built binaries. Most people just use whatever comes with their distribution of Linux so I can just like look, I can just get that VM, I can download the debug symbols and I can figure out all the offsets myself. You can statically link everything if you want and then you still have the problem of getting over and otherwise but you don't have to worry about your global offset tables getting poisoned. The problem with that is your binaries get pretty dirty. I don't know. Perhaps there's some really crazy legacy C code that if it's put in read-only memory will actually die but in theory that's supposed to work so that would actually work. You can also not load DSOs at runtime. The problem with that then is you sacrifice any and all plug-in architectures that are out there. You can disable P-Trace but P-Trace is pretty sweet and if you disable P-Trace you lose GDB and you lose S-Trace and both of those are really fucking useful. All day long and somebody injects a library in it will appear on proctpid maps and you can see whatever evil.so is now running in my process so that would actually work. It's just kind of painful to do that. So anyway, sort of looking forward on more experimental stuff I think for me personally my future research is going to be geared toward exploring alternative binary formats and other crazy things you can do. In the recap we talked about three different binary patching techniques. These techniques leave the original binary in working fashion after they're done. So kind of like teeth whitening, you can still eat food after you're done and just looks a little different, same deal. There's three things that matter. There's alignment, calling convention, and object file and library formats. Before I end this I actually had a couple extra slides of something really weird I found in semester time. So look an alien baby. And I saw this really crazy post to a mailing list. The title of the post was interesting behavior on OSX. This was written by a guy named Steven Edwards on November 29, 2007. This is the link to the actual mailing list post that he wrote. It turns out that Leopard actually has a P.E. loader which is really weird. So if you execute this code on a Leopard system and load a Win32 executable, you get this output. So for whatever reason there's a loader for Win32 binaries on OSX. So even if you had all the Windows DLLs in place in the right path so everything could be found, it wouldn't actually work anyway because all the APIs are different. But it's still sort of curious like why the hell is there a P.E. loader in OSX. So I don't know if anybody out there knows the answer to that question but definitely something to check out if you haven't seen it recently on Snow Leopard and it didn't work. But I remember trying this a few months ago on Snow Leopard and I thought it did work so I'm not really sure what's going on with that but it's pretty bizarre that there's a P.E. loader in Leopard. Anyway, thanks for coming and there's a bunch of links at the bottom. If you get this talk online and you want to read more about some of this stuff I have a bunch of blog posts describing each of the binary patching mechanisms in much more detail.