int (*)() 是指向具有以下原型的函数的指针类型:
int func();
由于语言的解析方式和运算符的优先级,必须将星号放在括号中。同样,在声明该类型的指针变量时,变量的名称在星号之后而不是在类型之后,例如不是
int (*)() ret;
而是
int (*ret)();
在您的情况下,ret 变量正在声明和初始化,涉及类型转换。
要通过函数指针调用函数,您可以使用更复杂的语法:
(*ret)();
或更简单的一个:
ret();
最好使用前一种语法,因为它向代码的读者表明ret 实际上是指向函数的指针,而不是函数本身。
现在,原则上该代码实际上不应该工作。 code[] 数组放置在初始化的数据段中,在大多数现代操作系统中是不可执行的,即调用 ret(); 应该会产生分段错误。例如。 Linux 上的 GCC 将 code 变量放在 .data 部分:
.globl code
.data
.align 32
.type code, @object
.size code, 93
code:
.string "1\3001\3331...\200"
然后.data 部分进入一个不可执行的读写段:
$ readelf --segments code.exe
Elf file type is EXEC (Executable file)
Entry point 0x4003c0
There are 8 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001c0 0x00000000000001c0 R E 8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000064c 0x000000000000064c R E 100000
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
LOAD 0x0000000000000650 0x0000000000500650 0x0000000000500650
0x0000000000000270 0x0000000000000278 RW 100000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DYNAMIC 0x0000000000000678 0x0000000000500678 0x0000000000500678
0x0000000000000190 0x0000000000000190 RW 8
NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c
0x0000000000000020 0x0000000000000020 R 4
GNU_EH_FRAME 0x0000000000000594 0x0000000000400594 0x0000000000400594
0x0000000000000024 0x0000000000000024 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 8
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version
.gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini
.rodata .eh_frame_hdr .eh_frame
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
04 .dynamic
05 .note.ABI-tag
06 .eh_frame_hdr
07
该段缺少可执行标志,即它只有RW 而不是RWE,因此无法从该内存执行任何代码。事实上,运行程序会导致存储在code 中的第一条指令出现错误:
(gdb) run
Starting program: /tmp/code.exe
Shellcode Length: 92
Program received signal SIGSEGV, Segmentation fault.
0x0000000000500860 in code ()
(gdb) up
#1 0x00000000004004a7 in main () at code.c:27
27 ret();
(gdb) print ret
$1 = (int (*)()) 0x500860 <code>
为了让它工作,你可以使用posix_memalign和mprotect的组合来分配一个内存页面并使其可执行,然后将code[]的内容复制到那里:
// For posix_memalign()
#define _XOPEN_SOURCE 600
#include <stdlib.h>
// For memcpy()
#include <string.h>
// For sysconf()
#include <unistd.h>
// For mprotect()
#include <sys/mman.h>
size_t code_size = sizeof(code) - 1;
size_t page_size = sysconf(_SC_PAGESIZE);
int (*ret)();
printf("Shellcode Length: %d\n", code_size);
posix_memalign(&ret, page_size, page_size);
mprotect(ret, page_size, PROT_READ|PROT_WRITE|PROT_EXEC);
memcpy(ret, code, code_size);
(*ret)();
另请注意,shell 代码使用int 0x80 调用 Linux 内核。如果程序是在 64 位 Linux 系统上编译的,这将无法开箱即用,因为使用不同的机制进行系统调用。在这种情况下应指定-m32,以强制编译器生成 32 位可执行文件。