int (*)()
是一个指针的类型与原型如下的函数:
int func();
由于的方式的语言被解析并且操作符的优先级,必须将星号放在括号中。另外,当声明该类型的指针变量时,变量的名称在星号之后,而不是在类型之后。它不是
int (*)() ret;
而是
int (*ret)();
在你的情况ret
变量既被声明,并与参与类型转换初始化。
要调用通过函数指针的函数,你既可以使用更复杂的语法:
(*ret)();
或更简洁:
ret();
使用前语法是因为它最好向您的代码的读者指示ret
实际上是指向函数的指针,而不是函数本身。
现在,原则上说,代码不应该实际工作。 code[]
阵列被置于初始化数据段中,在大多数现代操作系统中该数据段不可执行,即呼叫ret();
应该产生分段错误。例如。GCC在Linux上放置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位可执行文件。
这不是内核代码。所以'linux-kernel'不是合适的标签。 –
将一个字符指针转换为函数指针!现在这是未定义的行为。您只能将函数指针(任何类型)分配给函数指针。 – ajay