Introduction to shellcode development on Windows (1)
This article briefly introduces shellcode development technology and its features. Understanding these concepts can help us write our own shellcode. Further, you can modify existing vulnerabilities and use code to execute the custom functions you need.
I. Introduction
For example, if you have an Internet Explorer or Flash playerd code, you can only open the computer calc.exe. But this is actually useless, isn't it? What you really want is to execute some remote commands or implement other useful functions.
In this case, you may want to use the existing standard shellcode, such as the Shell Storm database or the msfvenom tool of Metasploit. However, you must first understand the basic principles of shellcode to use it effectively in your own vulnerability exploitation code. If you are not familiar with this term, refer to Wikipedia:
In computer security, shellcode is a small piece of code that can be used to exploit software vulnerabilities. It is called "shellcode" because it usually starts a command terminal through which attackers can control the affected computer, however, all code snippets that execute similar tasks can be called shellcode ....... Shellcode is usually written in the form of machine code.
Shellcode is a machine code that can be used to exploit the vulnerability. What is "machine code? Let's take the following C code as an example:
#include
int main(){ printf("Hello, World!\n"); return 0;}
The C code will be compiled into the following assembly code:
_main PROC push ebp mov ebp, esp push OFFSET HelloWorld ; "Hello, World!\n" call _printf add esp, 4 xor eax, eax pop ebp ret 0_main ENDP
Here, we need to pay attention to the main program and the call to the printf function. As highlighted in the debugger, the Code has been compiled into machine code:
So, "55 8B EC 68 00 B0 33 01... "Is the machine code of the above C code.
Ii. How to Apply shellcode to vulnerability Exploitation
For example, a stack-based buffer overflow vulnerability is used to exploit a simple vulnerability.
Void exploit (char * data) {char buffer [20]; // the buffer is located on the stack strcpy (buffer, data); // use strcpy to copy data}
The following describes how to exploit this vulnerability: (Note that this article does not describe how a buffer overflow vulnerability is exploited)
1) Send a string of more than 20 bytes to the application, including shellcode. 2) The stack structure is damaged because the written data crosses the boundaries of the static allocation buffer. Shellcode will also be placed on the stack. 3) A string uses a custom memory address to overwrite an important piece of data on the stack (such as the stored EIP or function pointer). 4) The program will jump to your shellcode from the stack, start executing the machine code command.
If you can successfully exploit this vulnerability, you can also run your own shellcode and actually use this vulnerability to do something useful, not just to crash the program. For example, shellcode can open a command terminal, download and execute files, restart the computer, enable remote desktop, or perform other operations.
Iii. Shellcode features
But shellcode cannot be any machine code. When writing our own shellcode, we must pay attention to some shellcode restrictions:
1) The direct offset of the string cannot be used. 2) the address of the function (such as printf) cannot be determined. 3) You must avoid some specific characters (such as NULL bytes) about each of the above questions. Let's have a brief discussion.
1. Direct offset of the string
Even if you define a global variable in C/C ++ code, a string with a value of "Hello world" or directly passing the string as a parameter to a function. However, the compiler places the string in a specific Section (such as. rdata or. data ).
Because we need code unrelated to the location, we want to use the string as part of the code, so we must store the string on the stack, as you will see later in this article.
2. Function address
In C/C ++, it is very easy to call a function. We use "# include <>" to specify a header file and call a function by name. The compiler and the linker will help you solve this problem: Resolve the address of the function (for example, the MessageBox function from user32.dll), and then we can easily call the function by name.
In shellcode, we can't wait for nothing. Because we cannot determine whether the DLL file containing the required functions has been loaded into the memory. Due to ASLR (address space layout randomization) mechanism, the system does not load the DLL file to the same address every time. In addition, the DLL file may change with every new update released by Windows, so we cannot rely on a specific offset in the DLL file.
We need to load the DLL file to the memory, and then directly find the required function through shellcode. Fortunately, Windows API provides us with two functions: LoadLibrary and GetProcAddress. We can use these two functions to find the function address.
3. Avoid NULL bytes
The value of NULL is 0 × 00. In C/C ++ code, null bytes are considered as the string Terminator. Because of this, the existence of a blank byte in shellcode may disrupt the functions of the target application, and our shellcode may not be correctly copied to the memory.
Although it is not mandatory, it is very common to use the strcpy () function to trigger the buffer overflow vulnerability. This function copies the string byte until it encounters NULL bytes. Therefore, if the shellcode contains NULL bytes, The strcpy function terminates the copy operation at the Null Byte, causing incomplete shellcode on the stack. As expected, shellcode certainly does not run normally.
The two commands in are functionally equivalent, but you can clearly see that the first command contains NULL bytes, while the second command contains NULL bytes. Although NULL bytes are very common in compiled code, they can be easily avoided.
Also, in some special cases, shellcode must avoid characters similar to \ r or \ n, or even letters and numbers.
Iv. shellcode comparison between Linux and Windows Platforms
Compared with Windows, it may be easier to compile Shellcode for Linux. This is because on the linux platform, we can easily execute system calls similar to write, execve, or send through 0 × 80 interruptions.
For example, to execute "Hello world" shellcode on linux, you only need to perform the following steps:
1) specify the number of syscall calls (for example, "write "). 2) Specify the syscall parameter (for example, stdout, "Hellow, world", and string length) of the system call. 3) Call 0x80 interrupt to execute syscall.
This will initiate a call: write (stdout, "Hello, world", length ).
On Windows, the situation is more complex. To generate a more reliable shellcode, we need to take more steps:
1) obtain the kernel32.dll base address; 2) locate the address of the GetProcAddress function; 3) Use GetProcAddress to determine the address of the LoadLibrary function; 4) then use LoadLibrary to load the DLL file (such as user32.dll); 5) use GetProcAddress to find the address of a function (such as MessageBox); 6) Specify the function parameter; 7) Call the function.
V. Conclusion
This article is part 1 of the Windows shellcode getting started series. This article describes what shellcode is, what restrictions shellcode has, and the differences between Shellcode on Windows and Linux platforms. Part 1 will briefly introduce the assembly language, PE () file format, and PEB (process environment block ). Next, you will understand how the content helps you write custom shellcode.