Windows Exploit Development – Part 5: Locating Shellcode With Egghunting

Written on:January 5, 2014
Comments are closed


In Part 4 we looked at how to find and execute your shellcode using various jump methods. In Part 5 we’re going to look at another method to find your shellcode called Egghunting. This method is especially useful when you’re faced with a small, reachable buffer (in which you can execute code) but the placement of your larger shellcode in memory is unpredictable. This post will get into quite a bit of detail, but I’ll try and explain everything as clearly as possible. Let’s dive in…

Introduction to the Win32 Egghunter

When we examined using jumps to reach shellcode in Part 4, there was one thing that we required — a predictable location for our shellcode. Even if our registers only pointed to a relatively small portion of our buffer, as long as we could use that space to jump to another known location containing our shellcode, we could execute our exploit. But what happens when you have that small portion of your buffer available but can’t use it to reach your shellcode with a typical jump technique (either because there are no available jump instructions, it’s too far, or it’s location is dynamic/unpredictable)? For those situations, we can use a technique called Egghunting. With Egghunting, we’ll use the minimal buffer space (reachable by our EIP overwrite) to host a small payload that does nothing more than search memory for the shellcode and jump to it. There are two basic pre-requisites to be able to use the Egghunter technique.

  • First, you must have a minimum amount of predictable memory to which you can jump that holds the small Egghunter code.
  • Second, your shellcode must be available in its entirety somewhere in memory (on the stack or heap). 

Keep in mind because we’re dealing with a limited buffer space, the Egghunter itself should be as small as possible to be useful in these situations. To understand the details behind Egghunting, your first resource should be Matt Miller’s (skape) paper titled “Safely Searching Process Virtual Address Space”. In it, he describes the various methods in which one can use Egghunters to search available memory in order to locate and execute otherwise difficult-to-find exploit code. He provides several Linux and Windows-based examples, some optimized more than others. For the purposes of this tutorial I’m only going to focus on the smallest (only 32 bytes), most optimized Windows version, which uses NtDisplayString. Please note that this method only works on 32-bit NT versions of Windows. All the examples that follow were tested on Window XP SP3. I’ll limit the discussion for now until I get into 64-bit Windows-based exploits in later posts. 

Using the Egghunter

Here’s how it works: 

  • Prepend your shellcode with an 8-byte tag (the “egg”).
  • Use the EIP overwrite to jump to a predictable location that holds a small Assembly language routine (the “Egghunter”) which searchers memory for the “egg” and, when found, jumps to it to execute the shellcode.

The egg will be a 4 byte string, repeated once. Let’s say our string is “PWND”, the egg we will prepend to our shellcode will be PWNDPWND. The reason for the repetition is to ensure that when we locate it in memory, we can verify we’ve actually found our shellcode (and not a random collection of 4 bytes, or the Egghunter routine itself) — it’s simply a way to double check we’ve reached our shellcode.

The Egghunter we’re going to implement will use (abuse) NtDisplayString, a read-only function that is designed to take a single argument — a pointer to a string — and display it.

Instead of using the function to display strings as intended, we’re going to sequentially work our way through memory address pointers and pass them to it, one at a time. If the function returns an access violation error when it attempts to read from that memory location, we know we’ve reached an unaccessible portion of memory and must look elsewhere for our shellcode. If it doesn’t return an error, we know we can examine the contents of that memory location for our egg. It’s a simple and elegant solution to testing the availability of memory to look for our egg. Here’s the code (adapted from Skape’s original version found here). Note: in that version, he uses NtAccessCheckAndAuditAlarm instead of NtDisplayString. As he explains in his paper (see earlier link) they both serve the same purpose and the only difference in terms of the code is the syscall number. 

I’ve included a C version below in case you want to compile it and load it into a debugger as a stand-alone .exe to follow along (please note that your addresses are likely going to vary). 

3.8 KiB

Let’s walk through the code in detail, starting from loop_inc_page. First, the or instruction cues up the next memory page to search by adding page_size – 1 (or 4095) to the current address in EDX and stores the result in EDX. The next instruction increments the value of EDX by 1. This effectively brings us to the very first address in the page we want to search. You might wonder why we just didn’t put 4096 into EDX, instead of breaking it into two instructions. The reason is because we need to maintain two separate loops — one to loop through each page and the other to loop through each address of a valid page one by one.


As we increment through each address, we make the call to NtDisplayString to see if it’s valid. Before we do, the value in EDX must be saved to the stack since we need to return to that location after the syscall; otherwise it will be clobbered by the syscall instruction. After saving EDX, we load the syscall number of NtDisplayString (43) into EAX. [If you want to find the numbers to the various Windows syscalls, check out this resource: ]


With EDX saved and the syscall parameter loaded into EAX, we’re ready to issue the interrupt and make the syscall. Once the syscall is made, EAX will be loaded with 0x5 if the attempt to read that memory location resulted in an access violation. If this happens, we know we’re attempting to read from an inaccessible memory page, so we go back to loop_inc_page and the next memory page is loaded to into EDX


This page loop will continue until a valid memory page is found.


Once a valid memory address is found, the execution flows diverts to is_egg. Now that it’s located a valid address, the next step is to compare our egg to the contents of that address. To do so, we load the egg into EAX and move (copy) our valid address from EDX to EDI for use by the next SCASD instruction.

You might wonder why we don’t just compare the value in EAX to the value in EDX directly. It’s because using the SCASD instruction is actually more effecient since it not only sets us up for the following jump instruction but it also automatically increments EDI by 4 bytes after each comparison. This allows us to check both halves of the egg and immediately jump to our shellcode once an egg is found, without the need for unnecessary Assembly instructions.

If the contents of EAX and the contents pointed to by the memory address in EDI don’t match, we haven’t found our egg so execution flow loops back to the INC EDX instruction which will grab the next address within the current page for comparison. 


Once the first half of the egg is found, the SCASD instruction is repeated to check for the second half. If that’s also a match, we know we’ve found our egg so we jump to EDI, which thanks to the SCASD instruction, now points directly to our shellcode.


Now that you understand how the Egghunter works, let’s see how to incorporate it into our exploit payload. I’ll once again use the CoolPlayer exploit from Part 4. If you recall, from Part 4, at the time of EIP overwrite, ESP points to only a small portion of our buffer — too small for our shellcode, but more than enough space for an Egghunter. Let’s update our previous exploit script.

First, we need to obtain the opcodes for the Assembly instructions and convert them to hex format for our Perl script. Depending on how you write the Egghunter (MASM, C, etc) there are varying ways in which you can extract the associated opcode. For this demo, I’m simply going to grab them from Immunity during runtime of my Egghunter executable (compiled from the C code I provided earlier). 


If you use this method, you can copy it to the clipboard or export it to a file and then convert it to script-friendly hex using any number of command line scripts such as this:

This results in the following output:

For the purposes of this demo, I’ll break up the hex with comments so you can easily match it to the corresponding Assembly instruction. Here it is incorporated into the exploit script we wrote in Part 4:

Also note I added the $egg and incorporated both it and the Egghunter into the $sploit portion of the buffer. Try the resulting .m3u file in CoolPlayer+ and you should get …

Let’s take a closer look to see what happened. The following screenshot of the corresponding memory dump shows where this access violation occurred:


If you look closely, you’ll note that although we see the start of our shellcode (prefaced by “PWNDPWND”) the shellcode is not intact, which is what caused our exploit to crash. This corrupted version of our shellcode is the first to appear in memory and the Egghunter is not smart enough to know the difference — it’s only designed to execute the instructions after the first “PWNDPWND” it finds. An Egghunter exploit might still be possible, provided our shellcode resides intact somewhere in memory.

We can use mona to find out:


The first two entries marked as “[Stack]” both appear in the previous screenshot and both are corrupted versions of our shellcode. That leaves the third entry from the Heap. Double-click that entry to view it in memory. 


Perfect, it’s intact. But how do we get our otherwise “dumb” Egghunter to skip the first two corrupted entries in memory and execute the third? We have a few choices.

Overcoming Corrupted Shellcode

If we have a scenario that calls for the use of an Egghunter but successful exploit is being hindered by the presence of multiple, corrupted copies of our shellcode we could:

  • Change the offset to the shellcode
  • Change the starting memory page of the Egghunter search 
  • Split the shellcode into smaller pieces (“Omelette” Egghunter)
  • Add some additional error checking to our Egghunter (“Egg Sandwich” Egghunter)

Change the Shellcode Offset 

One of the simplest methods of addressing this problem is to “push” the shellcode further into memory so the early copies are never made and (hopefully) the first copy the Egghunter reaches is intact.

Let’s try it with our CoolPlayer exploit. Add a new variable $offset and insert it into the buffer as follows: 


Run the new .m3u file and…


You can see why this worked by running the mona search again:


This time the offset pushed the shellcode far enough into our buffer so that no corrupted copies were placed on the stack and only the intact copy from the heap remains. 

Change the Starting Memory Page of the Egghunter

If we can predict where the corrupted copies are going to reside, we can simply tell the Egghunter to start looking after those memory addresses. This could probably be done any number of ways, but for this demo I’ll use an existing register and the ADD instruction. 

From the previous mona search, we know both corrupted copies reside at 0x0012F1AC and 0x0012F31C so all we need to do is start our Egghunter after these addresses. To do so, we need to change the value of ebx before the first memory page is loaded.

Launch the exploit as-is and pause execution at the very beginning of the the Egghunter routine to examine the stack. Specifically, look at ESP:


We need to start beyond 0x0012F31C. Subtract ESP from that and you get: 0x190 or 400 decimal. Therefore we can load EDX with ESP and then add at 400+ to EDX to push the starting memory page beyond the corrupted shellcode. An updated version of the Egghunter is below. Note I had to break up the ADD EDX instruction to avoid NULLs.  

 Here is EDX (and our new starting memory page) after executing the new mov/add instructions:


We’ve successfully pushed past the corrupted shellcode. Continue execution and …


Since one of the key features of a useful Egghunter is to be as small as possible, these extra 14 bytes of instructions can be seen as a negative, but if you have the space, it’s a viable option. Alternatively, you may consider trying to come up with more efficient methods of loading EBX with a larger address.

The Omelette Egghunter

The idea behind the Omelette Egghunter is to break up your shellcode into multiple chunks, each prefaced with its own egg as well as an additional tag that contains two pieces of information: 1) an indicator as to whether it is the last chunk of shellcode and 2) the length of the shellcode chunk. 

This approach can be useful if you know your shellcode gets corrupted when kept in large chunks but can stay intact if its divided into small enough pieces. At a high level it works like this:

Let’s say this is your shellcode:

$shellcode = \x41\x41\x41\41\x42\x42\x42\x42\x43\x43\x43\x43;

Left as-is, there is not enough space in memory to house it in its entirety, so we want to break it up into three chunks. We’ll use the same egg (PWNDPWND). We also need to append a two byte tag to this egg. The first byte is the chunk identifier — you can use any identifier but the last chunk must be different that the preceding chunks so the Egghunter knows when it has reached the end of the shellcode. You could use \x01 for the last chunk and \x02 for all preceding chunks. The second byte is the size of the shellcode chunk. In this rudimentary example, all three chunks will be 4 bytes in length so the second byte of the tag will be \x04. Note that since the size is stored as a single byte, each chunk is limited to 255 bytes in size.

So, the three chunks will look like this:

The Omelette Egghunter code locates each of the chunks and writes them, in order, to the stack to reassemble and execute the shellcode. I’m not going explain the Omelette Egghunter code but I encourage you take a look at an example here:

It’s a very useful concept but does have some flaws. First, the shellcode chunks must be placed into memory in order, something you might not have control over. Second, the reassembled shellcode is written to ESP and you risk writing over something important, including the Egghunter itself. (I’ve experienced both of these problems). Third, to take advantage of this added functionality, you sacrifice size — the omelette example found at the above link is 53 bytes vs. 32 bytes for the NtDisplayString Egghunter. Also, similar to the NtDisplayString Egghunter, it will grab the first egg-prepended shellcode it reaches in memory without means to verify whether it is a corrupted copy.

Despite these potential shortcomings, the Omelette Egghunter might be right for certain situations so keep it in mind. 

The Egg Sandwich

When I was considering various solutions for broken shellcode I thought it should be possible to have the Egghunter validate the integrity of the shellcode before executing to ensure it had found an intact version. That way, there would be no need to worry how many corrupt versions of the shellcode might reside in memory and no reason to worry about changing offsets or memory pages. Also, in exploits such as the one for CoolPlayer, since an intact copy does reside somewhere in memory, there would be no need to break the shellcode up into smaller chunks (as in the Omelette example).

Here’s my basic concept:

For the Egg Sandwich Egghunter you need two 8 byte eggs — one to prepend to the beginning of the shellcode and one to append to the end.

The prepended egg also contains a two byte tag similar to the Omelette Egghunter — the first byte identifies the egg number (\x01) and the second byte is the offset to the second egg (equal to the length of the shellcode). The second appended egg would also contain a two byte tag — the first byte is the egg number (\x02) and the second byte is the offset to the beginning of the shellcode (equal to the length of shellcode + length of the second egg).

Assuming we use our 227 byte calc.exe shellcode and our egg of PWNDPWND, the first egg in the Egg Sandwich would look as follows:

The second egg would look as follows.

Note the first egg’s size tag is \xe3 (or 227, the length of the shellcode) while the second is \xeb (shellcode + 8 = 235). 

The Egghunter code locates the first egg as normal. It then reads the egg number tag to verify it has found the first egg and uses the offset tag to jump the appropriate number of bytes to the second egg. It then checks to make sure the second found egg is in fact the appended egg (by verifying its number) and then uses the offset tag to jump back to the beginning of the shellcode to execute.  

Any corrupted copies of the shellcode that have had bytes added or subtracted in any way will fail the second egg check and be skipped. The only way a corrupted egg would pass this verification step would be if it maintained the exact same number of bytes as the original. 

Here is the Perl exploit script for CoolPlayer+ modified with the Egg Sandwich Egghunter code:

Give it a try and you should see…


I’ve also included the C version here in case you want to try it on its own:

Egg Sandwich
4.1 KiB

I wouldn’t be surprised if I wasn’t the first to think of this “Egg Sandwich” approach, though I couldn’t find any other references. It does have some disadvantages:

  • At 50 bytes, it’s 18 bytes larger than the NtDisplayString Egghunter.
  • In its current state it accommodates a single byte for the offset size tag, meaning the shellcode is limited to 255 bytes or smaller. That could be adjusted, though it will likely increase the size of the Egghunter code.

Anyway, at the very least it may get you thinking of other ways to implement Egghunters or maybe even improve upon this one. 


Hopefully, this installment of the Windows Exploit Development Series provided a thorough introduction to the Egghunter technique and how it can help execute your shellcode even when you’re faced with a limited amount of reachable buffer space. Stay tuned for Part 6, where I’ll cover Structured Exception Handler (SEH) based exploits, a more reliable alternative to the standard EIP overwrite.  

Related Posts:

5 Comments add one

  1. Jose Ramon Garcia says:

    Hello Mike,
    Again reading you great post. Thank you so much for it, it’s very useful.
    I write you because I don’t really understand the reasons to use egg hunting, it’s due I’m novice on that subject, If you want, you can answer me private.
    When you says
    ” But what happens when you have that small portion of your buffer available but can’t use it to reach your shellcode with a typical jump technique (either because there are no available jump instructions, it’s too far, or it’s location is dynamic/unpredictable)? ”

    I thought, if you can control EIP and redirect to a CALL or JUM which lands to our buffer, then what is the problem to redirect from there to our shell code ???.

    I would appreciate your clarification.

    Thank you

    • Mike Czumak says:

      Thanks Jose. Sometimes, controlling EIP is not enough to be able to reach your shellcode with a single jump instruction. For example, what if you’re faced with a scenario where you know the shell code is intact but it’s in a location that you can’t find a reliable jump instruction to? Or, maybe you can jump to a location but it’s not big enough to hold your entire payload and you know you’re shellcode does exist somewhere in memory (possibly at an unpredictable location). In this case if you can redirect program execution to your egghunter, you can let it do the work of finding the exact location of your shellcode and executing it. I’ve sent you an email just in case you want additional clarification.

  2. AK says:

    Thank you very much for such a great breakdown of the egg hunter code, Mike.

    Now, there is something that’s still bugging me about this particular code: the EDX register is not first zeroed out. In the first example used here, the initial value of EDX happens to be 00000000, so after the first two egg hunter instructions, we have:

    OR DX, 0x0FFF // EDX = 00000FFF
    INC EDX // EDX = 00001000

    However, unless there is some mechanism that guarantees EDX will always start off as zero, could there not be cases in which our egg hunter would miss the shellcode?

    For example, let’s say after we launch an exploit against some vulnerable software, EDX somehow winds up to be 22220000. In this case, the first two instructions of the egg hunter will set EDX to equal 22220FFF, then 22221000. Now, if our shellcode gets stored at memory location 11001100, then the egg hunter would fail.

    Is this a realistic scenario, or am I missing something?

    • Mike Czumak says:

      Good question and I think it can be best answered by Matt Miller’s paper that I referenced within the post:

      “The logic behind that is that incrementing by PAGE SIZE allows for quick searching through invalid memory regions, and thus obviates the need to initialize the register to a given value due to the fact that it will wrap around as many times as necessary.” Reference: Safely Searching Process Virtual Address Space

      In other words, by incrementing EDX by page size you should eventually reach your shellcode regardless of the initial value of EDX. In fact, you might notice that if you change the tag on your shellcode to an invalid value, your egghunter will enter a continuous search loop. Of course, you can add an instruction prior to the OR DX instruction that initializes EDX to a specific value (say for example your shellcode happens to reside at the beginning of the current memory page and you want to reach it faster). If you want an example, see the Corelan Tutorial section called “Tweaking the egg hunter start position (for fun, speed and reliability)” here:

      I hope this answers your question.

      – Mike

  3. Osho says:

    Hey Mike, this is an excellent series which I really hope you continue doing. Would love to see some unique ASLR or DEP bypasses as well as shellcoding in Windows, since it’s completely off the wall compared to Unix where you can simply use system calls.

    I have a question regarding corrupt shellcode in memory: what causes the corruption? I am currently doing the CTP course and have successfully completed Module 6 (Winamp), using egghunting as the technique to reach shellcode. Now, strangely, when I ran the exploit today, it crashed Winamp. I looked at it with Immunity and noticed my shellcode is corrupted.

    I am really curious as to what caused this, since the exploit worked perfectly for about 2 weeks (this is on my personal Win 7 VM). Searching for my egg tag in mona shows an instance of my uncorrupted shellcode in the heap, similar to the example with Cool Player above. I’m assuming I would have to change the start of the memory location to where the egghunter begins, since changing the offset wouldn’t work for this particular exploit because of the limited buffer space, as compared to the 10000 in the Cool Player exploit. Currently not at home so haven’t tried any of this out yet, but am just really curious as to how the shellcode can get corrupted after proving to be successful so many times.

    Again, really hope you continue this exploit development series as it is definitely one of the best on the net. Cheers!