When a function ends with a switch statement you often see a lot of jumps to a static address that then just returns.
Below is an example of this pattern.
1170: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
1177: eb 3e jmp 11b7 <main+0x8e>
1179: c7 45 f8 02 00 00 00 movl $0x2,-0x8(%rbp)
1180: eb 35 jmp 11b7 <main+0x8e>
1182: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)
1189: eb 2c jmp 11b7 <main+0x8e>
118b: c7 45 f8 04 00 00 00 movl $0x4,-0x8(%rbp)
1192: eb 23 jmp 11b7 <main+0x8e>
1194: c7 45 f8 05 00 00 00 movl $0x5,-0x8(%rbp)
119b: eb 1a jmp 11b7 <main+0x8e>
119d: c7 45 f8 06 00 00 00 movl $0x6,-0x8(%rbp)
11a4: eb 11 jmp 11b7 <main+0x8e>
11a6: c7 45 f8 07 00 00 00 movl $0x7,-0x8(%rbp)
11ad: eb 08 jmp 11b7 <main+0x8e>
11af: c7 45 f8 08 00 00 00 movl $0x8,-0x8(%rbp)
11b6: 90 nop
11b7: 8b 45 f8 mov -0x8(%rbp),%eax
11ba: 5d pop %rbp
11bb: c3 retq
Ropper misses these gadgets, I'm assuming because the search doesn't follow forward edges.
Even when using very permissive search results it doesn't find these gadgets.
Here are the example code I used to generate the test binary and the command I ran to check for gadgets.
#define CASE_RET(x) case x: return_val = (x+1); break;
int main(int argc, char** argv)
{
int x = 0;
int return_val = 0;
switch(x)
{
CASE_RET(0)
CASE_RET(1)
CASE_RET(2)
CASE_RET(3)
CASE_RET(4)
CASE_RET(5)
CASE_RET(6)
CASE_RET(7)
}
return return_val;
}
Compiling and checking for ret type gadgets that start with mov only finds the last switch case and the mov from [rbp-8] to eax but doesn't find all other integer moves into [rbp-8}
gcc test.c
ropper -f a.out --type rop --quality 100 --inst-count 20 --nocolor | grep ': mov'
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x000000000000110c: mov byte ptr [rip + 0x2efd], 1; pop rbp; ret;
0x00000000000011af: mov dword ptr [rbp - 8], 8; nop; mov eax, dword ptr [rbp - 8]; pop rbp; ret;
0x00000000000011b7: mov eax, dword ptr [rbp - 8]; pop rbp; ret;
0x0000000000001009: mov eax, dword ptr [rip + 0x2fd9]; test rax, rax; je 0x1016; call rax; add rsp, 8; ret;
0x00000000000010f7: mov ebp, esp; je 0x1107; mov rdi, qword ptr [rip + 0x2f06]; call 0x1030; call 0x1070; mov byte ptr [rip + 0x2efd], 1; pop rbp; ret;
0x00000000000010fc: mov edi, dword ptr [rip + 0x2f06]; call 0x1030; call 0x1070; mov byte ptr [rip + 0x2efd], 1; pop rbp; ret;
0x0000000000001008: mov rax, qword ptr [rip + 0x2fd9]; test rax, rax; je 0x1016; call rax; add rsp, 8; ret;
0x00000000000010f6: mov rbp, rsp; je 0x1107; mov rdi, qword ptr [rip + 0x2f06]; call 0x1030; call 0x1070; mov byte ptr [rip + 0x2efd], 1; pop rbp; ret;
0x00000000000010fb: mov rdi, qword ptr [rip + 0x2f06]; call 0x1030; call 0x1070; mov byte ptr [rip + 0x2efd], 1; pop rbp; ret;
When a function ends with a switch statement you often see a lot of jumps to a static address that then just returns.
Below is an example of this pattern.
Ropper misses these gadgets, I'm assuming because the search doesn't follow forward edges.
Even when using very permissive search results it doesn't find these gadgets.
Here are the example code I used to generate the test binary and the command I ran to check for gadgets.
Compiling and checking for
rettype gadgets that start withmovonly finds the last switch case and themovfrom[rbp-8]toeaxbut doesn't find all other integer moves into[rbp-8}