A makefile to build shellcodes
Spoiler: Instead of remembering (and typing) all the needed commands to build and inject our shellcodes, let’s delegate this pain to a makefile.
While writing our book about shellcodes, we had to write lots of them, with lots of variations to see which were more instructive and, of corse, we often had to test them all to check they worked as expected.
The book contains the commands to translate the shellcode source to their injectable form. They are quite simple but it’s always more convenient when routine tasks are automated.
And since the book used classic compilation tools, make
was also available which bring us the ability to build files when we
express the reciepes. The result was pretty usefull so we thought it was
worth sharing with you.
Let’s start with a first rule to write in your makefile
,
for the target all
, as follows:
all:
With that rule, you know that when you’ll run make
without argument, that target will be build. It does nothing but if you
want to build something, you can add any reciepe or dependency. Here or
below in the file.
Shellcode Example
There are plenty ways to write shellcodes. Some authors giv them as C strings (where the characters are expressed as hex codes) and other give them in assembly (Intel or AT&T syntax).
In our book, we chose to write instructions in assembly (AT&T syntax) along with their translation in hexadecimal codes (in comments), face to face. That way, it’s easier so explain and understand the translation.
It won’t be the most impressive experience of your life, but here is
how it looks with arthurdent.s
, a shellcode that
terminate with it specific error status 42 (cf. page 44).
ArthurDent:
$0x3c, %rax # 48 B8 3C 00 00 00 00 00 00 00
movabs $0x2a, %rdi # 48 BF 2a 00 00 00 00 00 00 00
movabs syscall # 0F 05
Build injectable files
Throught hexcodes
The advantage of putting the hexadecimal translation in comment, and
using the number sign (#
) to begin them, is that we can
automatically extract the translation with one sed
rule
(cf. page 21) and put the rule in a reciepe as follows:
%.hex: %.s
's/.*#//p' $< > $@ sed -n
This rule tell make that it can take any file ending with
.s
(our assembly files), one can produce a file with same
name but extention .hex
by using the bash command line on
the second line. This rule uses two automatic variables:
$<
which contains the first dependency (here, the file ending with.s
),$@
which contains the target (here the file ending with.hex
).
Once the hexcodes extracted from the file, one can convert it in
binary with xxd
and once again, the reciepe is quite
simple:
%.bin: %.hex
$< > $@ xxd -r -p
Thos two rules are sufficient to convert any of our shellcodes’ source code in a binary and injectable form. Here is an example of execution:
$ make arthurdent.bin
sed -n 's/.*#//p' arthurdent.s > arthurdent.hex
xxd -r -p arthurdent.hex > arthurdent.bin
rm arthurdent.hex
When we ask make to build the file, it managed itself to find and
execute the needed reciepes (sed
then xxd
).
And since those rules are generic, it deletes the temporary file.
Through assembly
The two previous reciepes need us to provide the translation in hexcodes. Since we do not always want to do this job, here are two other rules to produce the injectable shellcode through its assembly code (cf. page 24):
%.o: %.s
$@ $^
as -o
%.raw: %.o
$< $@ objdump -j .text -O binary
The first rule produce an object file containing the assembled code (by gas) and the second rule extract the instructions part to produce the injectable file.
Cleaning
To avoid polluting your directory with all those files produced by
the reciepes, we add a cleaning rule (traditionnaly called
clean
) as follows:
clean:
rm -f *.o *.raw *.hex *.bin
.PHONY: clean
The two first lines define the reciepe (delete the file corresponding
to the four extensions). The las rule is a bit special, it tells make to
consider the dependency (i.e. clean
) as not being
a file but a simple keyword (without this line, if you create a file
named clean
, newer than the shellcode, make will never
execute the clean reciepe).
Variation on Windows
To keep it simple, the book propose to use WinLibs to compiles the loaders and the shellcodes, keeping the sames tools as GNU/Linux (gcc, gas, objcopy, …). The reciepes to produce shellcodes through assembly are the same, but those through hexcodes need to be replaced.
The command used to extract hexcode (cf. page 196) can not be written in the makefile because make dislike Powershell (and vice versa).
We’ve then written the command in a powershell script file
grepsed.ps1
. Changing it a little to allows the script to
uses parameters from command line.
Select-String -Path $args[0] -Pattern "#" `
| foreach-object {([string]$_).split("#")[1] } `
> $args[1]
That way, the rule to produce hex file can be expressed in makefile: it tells make to launch the powershell script.
%.hex: %.s
$< $@ powershell grepsed.ps1
The reciepe to produce the binary file from the hex is quite simple.
We replace xxd
with certutil.exe
(and adapt
some parameters).
%.bin: %.hex
$< $@ certutil.exe -decodehex
Automatic injection
The previous rules are sufficient to produces the injectable files from the book’s source codes. But we might go further and also automate the injection.
Building the loader
To automate the injectino, we need an operationnal loader. The rule are more classical:
loader: loader.c
gcc -o loader loader.c
cleanall: clean
rm -f loader
.PHONY: cleanall
Inject the shellcode
We only need to write rules to execute our shellcodes by injecting them in the loader. These reciepes are special because:
- They are not written to produce any file, we should put them in
.PHONY
, - The rules in
.PHONY
can’t be generic… the classic way won’t work.
We’ll then use a
trick to force make to execute those reciepes, whether a file
already exists or not. This tric involve a target that have no
dependency and no reciepe (hereafter called .FORCE
) and put
it as dependency of the reciepe we want to force.
.FORCE:
%-bin: %.bin loader .FORCE
$<
./loader
%-raw: %.raw loader .FORCE
$< ./loader
With those rules, we can easily run the shellcodes whenever we update it (or the loader) as follows:
$ make arthurdent-bin
gcc -o loader loader.c
sed -n 's/.*#//p' arthurdent.s > arthurdent.hex
xxd -r -p arthurdent.hex > arthurdent.bin
./loader arthurdent.bin
make: *** [makefile:33 : arthurdent] Error 42
rm arthurdent.bin arthurdent.hex
The error 42 in the last but one line is the expected behaviour. The loader has executed the shellcode which terminate with 42 code. This command have been launched by make, it’s thus make that catched the code. As it is expected to do, make interpret this non-zero code as an error and show this to us (and that’s we wanted to).
And after?
Nothing prevent you from adapt and extend this makefile
.
AFAIK, we added thos three reciepes:
%-gas: %.s .FORCE
$< -al -o /dev/null
as
%-objd: %.o .FORCE
$<
objdump -d
%-strace: % loader .FORCE
$< strace ./loader
The first display the assembly produced by gas (cf. page
22), the second display the disassembly done by objdump (cf.
page 23). The third is not in the book and uses strace
to
show (and follow) the list of syscalls made by the loader and the
shellcode (usefull for debug).