I assume you are compiling your program in x86-64 mode, otherwise there would be no chance (nor need) for popping rdi
. Generally speaking, smaller programs have fewer gadgets. Particularly very simple programs tend not to have many gadgets that begin in the middle of long instructions, as longer programs/libraries may have.
Blocks compiled in if(0){}
will never appear in the output of the program, even when optimization is disabled, this is likely to be completely ignored by the compiler. Instead, you can use a global variable because it is hard for an optimizer to reason about the value of a global.
#include <stdio.h>
int foo = 0;
void callme() {
asm volatile ("pop %%rdi\n\t"
"ret"
:
:
: "rdi");
}
int main(int argc, char **argv) {
printf("Hello world!\n");
if (foo == 0x55) {
callme();
}
}
The assembly in the function callme performs a pop rdi;ret
sequence. Because it is in GCC, AT&T syntax is expected, so registers require a %
. Unfortunately, the asm
macro uses %
as a meta-character, requiring the doubling up to escape a single %
. asm
takes several arguments separated by colons (I do not know why they chose colons instead of commas). The first is the assembly template, which can include auto-assigned registers if the programmer does not require control of their own registers. The 2nd and 3rd are the destination (output) and source (input) registers. The final one is a list of other registers that are overwritten by the assembly. In normal use, this will tell the compiler how to allocate registers for this function. Given that this function is not actually being used normally, but only for shellcoding, all of this is mostly optional, but I went with the long version for demonstration purposes here.
If you compile this code with gcc -O0 -o sample sample.c
, and then dump the disassembly via objdump -d sample
, you will see the function callme
is included, and contains the gadget you are looking for.