Shellcode
called "shellcode" because it typically starts a command shell from which the attacker can control the compromised machine, but any piece of code that performs a similar task can be called shellcode.
Generating Shellcode
To generate our own shellcode, we need to write and extract bytes from the assembler machine code.
For this task, we will be creating a simple shellcode for Linux that writes the string "Hello World!". The following assembly code uses two main functions:
- System Write function (
sys_write) to print out a string we choose. - System Exit function (
sys_exit) to terminate the execution of the program.
To call those functions, we will use syscalls.
In this case, we will request the kernel to write a string to our screen, and the exit the program. Each operating system has a different calling convention regarding syscalls, meaning that to use the write in Linux, you'll probably use a different syscall than the one you'd use on Windows. For 64-bits Linux, you can call the needed functions from the kernel by setting up the following values:
| rax | System Call | rdi | rsi | rdx |
|---|---|---|---|---|
| 0x1 | sys_write | unsigned int fd | const char *buf | size_t count |
| 0x3c | sys_exit | int error_code |
The table above tells us what values we need to set in different processor registers to call the sys_write and sys_exit functions using syscalls.
For 64-bits Linux, the rax register is used to indicate the function in the kernel we wish to call. Setting rax to 0x1 makes the kernel execute sys_write, and setting rax to 0x3c will make the kernel execute sys_exit. Each of the two functions require some parameters to work, which can be set through the rdi, rsi and rdx registers. You can find a complete reference of available 64-bits Linux syscalls here:
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
- For
sys_write, the first parameter sent throughrdiis the file descriptor to write to. The second parameter inrsiis a pointer to the string we want to print, and the third inrdxis the size of the string to print. - For
sys_exit,rdineeds to be set to the exit code for the program. We will use the code 0, which means the program exited successfully.
We will have the folling in a file called hello.asm
- First, our message string is stored at the end of the .text section. Since we need a pointer to that message to print it, we will jump to the call instruction before the message itself. When call GOBACK is executed, the address of the next instruction after call will be pushed into the stack, which corresponds to where our message is.
- Note that the
0dh, 0ahat the end of the message is the binary equivalent to a new line (\r\n).
- Note that the
- Next, the program starts the GOBACK routine and prepares the required registers for our first
sys_write()function.
- We specify the
sys_writefunction by storing 1 in theraxregister. - We set
rdito 1 to print out the string to the user's console (STDOUT). - We pop a pointer to our string, which was pushed when we called GOBACK and store it into
rsi. - With the syscall instruction, we execute the
sys_writefunction with the values we prepared. - For the next part, we do the same to call the
sys_exitfunction, so we set0x3cinto theraxregister and call thesyscallfunction to exit the program.
Next, we compile and link the ASM code to create an x64 Linux executable file and finally execute the program.
Now that we have the compiled ASM program, let's extract the shellcode with the objdump command by dumping the .text section of the compiled binary.
Now we need to extract the hex value from the above output. To do that, we can use objcopy to dump the .text section into a new file called thm.text in a binary format as follows:
The thm.text contains our shellcode in binary format, so to be able to use it, we will need to convert it to hex first. The xxd command has the -i option that will output the binary file in a C string directly:
To confirm that the extracted shellcode works as we expected, we can execute our shellcode and inject it into a C program.
Then, we compile and execute it as follows,
Obfuscation
Malware obfuscation is a technique used by malicious actors to hide or disguise the true nature and functionality of their code, making it more difficult for security software to detect and for cybersecurity experts to analyze
Hashcat
Hashcat is an advanced password recovery tool used in cybersecurity practices. It is renowned for its versatile functionality, speed, and efficiency.