0x00 Preface
---
In 'Windows Shellcode Study Notes: Extraction and Testing of Shellcode', it introduced how to perform preliminary optimization on shellcode, dynamically obtain Windows API addresses and call them, and implement automatic extraction of machine code as shellcode and save it to a file through a program.
The bin file of the pop-up example shellcode has been uploaded to GitHub, with the address as follows:
An open-source project
Note:
shellcode.bin is generated by getshellcode.cpp
The address of getshellcode.cpp is as follows:
An open-source project
Next, we will study the usage and optimization techniques of shellcode in specific environments.
0x01 Introduction
---
Starting with the most basic buffer overflow
This article will combine the 'Stack Overflow Principles and Practice' chapter from '0day Security: Software Vulnerability Analysis Technology', using the stack overflow code from it as a sample, to optimize our own generated pop-up example shellcode and achieve preliminary exploitation in stack overflow.
0x02 Related Concepts
---
Stack Area:
Used to dynamically store the calling relationships between functions, ensuring that the called function returns to the calling function and continues execution.
Special Registers:
ESP: Stack Pointer Register (extended stack pointer), points to the top of the stack.
EBP: Base Pointer Register (extended base pointer), points to the bottom of the stack.
EIP: Instruction Pointer Register (extended instruction pointer), points to the address of the next instruction to be executed.
Function Code Storage Order in the Stack (intuitive understanding, other details omitted):
- buffer
- Previous Stack Frame EBP
- Return Address
- ESP
Principle of Function Stack Overflow (intuitive understanding, other details omitted):
Normally, during the function return process, the content saved in the return address is executed last, typically jumping to the address of the next instruction.
If the buffer length is too long, long enough to overwrite the return address value, then when the function returns, it will execute the overwritten content.
If shellcode is saved in the buffer and the overwritten return address is the starting address of the shellcode, then the shellcode will be executed, completing the exploitation of the stack overflow.
0x03 Stack Overflow Example Test
---
Sample code is as follows:
#include |
Note:
The code is selected from the experimental code in section 2.4.2, with minor adjustments.
Among them,
fscanf(fp,"%s",password) terminates when encountering spaces and newline characters. If the shellcode contains spaces (0x20), it will be truncated, resulting in incomplete file reading.
Therefore, it is replaced with fread(password,56,1,fp);
The array password has a length of 56, and the array buffer has a length of 44. When executing strcpy(buffer,password);, a stack overflow occurs.
According to the principle of function stack overflow, achieving stack overflow requires the following process:
(1) Analyze and debug the program to obtain the offset for overwriting the return address.
(2) Obtain the starting address of the buffer, overwrite the return address based on the obtained offset, causing the code stored at the buffer's starting address to execute upon function return
(3) Extract the machine code for the pop-up operation and save it at the starting address of the buffer, to be executed upon function return
Test system: Win XP |
(1) Analyze and debug the program to obtain the offset for overwriting the return address
Fill 56 test characters in password.txt, open the program with OllyDbg, locate the function return address
As shown in the figure

Return address is exactly overwritten
(2) Obtain the starting address of the buffer and overwrite the return address
As shown in the figure

Obtain the starting address of the buffer: 0012FB7C
Note:
The starting address of the buffer varies across different systems.
Overwrite the return address with 0012FB7C, meaning the hexadecimal characters at positions 53-56 in password.txt are 7CFB1200 (saved in reverse order).
(3) Extract the machine code for the pop-up operation.
Following the method in "0day Security: Software Vulnerability Analysis Techniques," use Dependency Walker to obtain the base address of user32.dll as 0x77D10000.
The offset address of MessageBoxA is 0x000407EA.
As shown in the figure.

Thus, the entry address of MessageBoxA in memory on this system is 0x77D10000 + 0x000407EA = 0x77D507EA.
Replace the machine code corresponding to the entry address of MessageBoxA in the book.
The final content of password.txt is as follows (hexadecimal view):
00000000h: 33 DB 53 68 77 65 73 74 68 66 61 69 6C 8B C4 53 ; 3跾hwesthfail嬆S
00000010h: 50 50 53 B8 EA 07 D5 77 FF D0 90 90 90 90 90 90 ; PPS戈.誻袗悙悙?
00000020h: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ; 悙悙悙悙悙悙悙悙
00000030h: 90 90 90 90 7C FB 12 00 ; 悙悙|?.
The final program runs as shown in the figure, with stack overflow successfully triggered on our test system.

0x03 Optimization of MessageBox Shellcode in Stack Overflow
---
The previous section briefly introduced the principles and operation methods of the stack overflow example. This section will explain how to optimize our self-developed shellcode, specifically the MessageBox shellcode example, and achieve exploitation by combining it with specific vulnerabilities.
Download link for the MessageBox shellcode example:
An open-source project
Shellcode length: 1536
(1) Modify the example program so that its array is sufficient to store our shellcode
Complete code is as follows:
#include |
Buffer length increased to 1556 to store the popup instance shellcode
Based on the previous example, the offset for overwriting the return address is 9-12, so the password length is increased to 1556+12=1568
(2) strcpy truncates when encountering character 00
As shown in the figure

The shellcode instance in the pop-up has a character 0x00 at address 00000009h. When strcpy executes, encountering 0x00 causes premature truncation, resulting in incomplete shellcode that cannot overwrite the return address.
Therefore, the shellcode needs to be encoded.
For the reader's convenience, following the method in Section 3.5.2 of '0day Security: Software Vulnerability Analysis Technology' (this section provides detailed explanations, so the process is not reiterated here):
- Add the termination character 0x90 to the end of the shellcode.
- Encrypt the shellcode byte by byte using XOR with 0x44.
- Assemble the decoder and extract the machine code.
- Place the decoder's machine code at the beginning of the shellcode.
- The decoder aligns EAX to the starting position of the shellcode, decrypts byte by byte using XOR with 0x44, and stops upon encountering 0x90.
The assembly code for the decoder is as follows:
void main() |
Using OllyDbg, the extracted machine code is as follows:
"\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80\xFB\x90\x75\xF1"
The new shellcode format is as follows:
Decoder machine code + Encrypted message box example shellcode + 0xD4 + "\x90\x90\x90\x90\x90\x90\x90" + "\x7C\xFB\x12\x00"
Note:
0x90 ^ 0x44 = 0xD4, where 0xD4 is the encoded end character
"\x90\x90\x90\x90\x90\x90\x90" is a padding string with no meaning
"\x7C\xFB\x12\x00" is the overwritten function return address
(3) 0xD4 conflict
As shown in the figure

The popup example shellcode also contains the termination character 0xD4, which would cause premature truncation during decryption, so a new termination character needs to be selected
Alternatively, the shellcode can be encrypted in segments. For this specific shellcode, 0xD5 happens to be absent, so 0xD5 is used as the termination string, with the decryption character being 0x91
The modified machine code is as follows:
"\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80\xFB\x91\x75\xF1"
The modified shellcode format is as follows:
Decoder machine code + encrypted popup example shellcode + 0xD5 + "\x90\x90\x90\x90\x90\x90\x90" + "\x7C\xFB\x12\x00"
(4) Shellcode encoding test
Write a program to automatically read the original shellcode, encrypt it, add decryption machine code, and append the termination character
The program has been uploaded to GitHub
An open-source project
After execution as shown, a new shellcode file is generated, and C-format shellcode is output to the screen

Using the following code, combined with the C-format shellcode output on screen, replace the array content to test the new encrypted shellcode
Due to the lengthy code, it has been uploaded to GitHub at the following address:
An open-source project
As shown, the shellcode executes successfully, achieving the decoder

(5) Testing the new shellcode in stack overflow
Fill in the decoder machine code, the complete shellcode format is as follows:
"\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80\xFB\x91\x75\xF1"+encrypted pop-up example shellcode+0xD5+"\x90\x90\x90\x90\x90\x90\x90"+"\x7C\xFB\x12\x00"
Still encountering errors in the stack overflow test program, use OllyDbg to load and continue debugging
As shown below, successfully overwriting the function return address, then press F8 for single-step debugging

As shown below, an exception is discovered at this point: the EAX register value is 909090D5. Normally, EAX should hold the starting address of the Buffer to successfully locate and decrypt the shellcode

However, the register EDX holds the starting address of the buffer
Therefore, we need to modify the decoder
(6) Modify the decoder
Choose the simplest and most direct method: align EDX to the starting position of the shellcode. The implemented assembly code is as follows:
void main() |
Load the program in OllyDbg and extract the machine code, as shown in the figure

The new decoder machine code is:
"\x83\xC2\x14\x33\xC9\x8A\x1C\x0A\x80\xF3\x44\x88\x1C\x0A\x41\x80\xFB\x91\x75\xF1"
The final shellcode is:
"\x83\xC2\x14\x33\xC9\x8A\x1C\x0A\x80\xF3\x44\x88\x1C\x0A\x41\x80\xFB\x91\x75\xF1"+encrypted message box example shellcode+0xD5+"\x90\x90\x90\x90\x90\x90\x90"+"\x7C\xFB\x12\x00"
The complete shellcode has been uploaded to GitHub at:
An open-source project
Test the stack overflow again, as shown in the figure, the shellcode executes successfully

Since the shellcode implements dynamic API address retrieval on its own, the LoadLibrary("user32.dll"); in the stack overflow test program can be omitted
0x04 Summary
---
This article provides a brief description of the principles of stack overflow, focusing on the optimization, debugging, and exploitation techniques of shellcode in specific stack overflow environments.
Of course, the aforementioned shellcode has a drawback: the starting address of the shellcode in memory is often not fixed, which may lead to unsuccessful vulnerability exploitation.
The next article will address this issue.