时间:2022-03-29 09:14:56 | 栏目:C代码 | 点击:次
上一个lab的主要内容为__data pointer__(指向数据的指针)可能在Linux系统中造成的__segmentation fault__。本次lab将考虑__function pointer__(指向函数/代码的指针)可能造成的错误:segfault或其他exceptions。
一个函数指针可以像函数一样被调用,包括传递参数和获得返回结果。函数指针的一些用途是用于编写泛型generic函数,有时是一种面向对象的样式,也用于实现回调。
定义形式
类型 (*指针变量名) (参数列表);
如, int (*p)(int i, int j);
→ p是一个指针,它指向一个函数,该函数有两个整型参数,返回类型为int;p首先和*结合,表明p是一个指针,再与( )结合,表明它指向的是一个函数。
调用函数指针
(*p) (argument)
p (argument)
例子
#include <stdio.h> #define GET_MAX 0 #define GET_MIN 1 int get_max(int i,int j) { return i>j?i:j; } int get_min(int i,int j) { return i>j?j:i; } int compare(int i,int j,int flag) { int ret; //这里定义了一个函数指针,就可以根据传入的flag,灵活地决定其是指向求大数或求小数的函数 //便于方便灵活地调用各类函数 int (*p)(int,int); if(flag == GET_MAX) p = get_max; else p = get_min; ret = p(i,j); return ret; } int main() { int i = 5,j = 10,ret; ret = compare(i,j,GET_MAX); printf("The MAX is %d\n",ret); ret = compare(i,j,GET_MIN); printf("The MIN is %d\n",ret); return 0 ; }
#include <stdio.h> #include <string.h> void check(char *a,char *b,int (*cmp)(const char *,const char *)); main() { char s1[80],s2[80]; int (*p)(const char *,const char *); //将库函数strcmp的地址赋值给函数指针p p=strcmp; printf("Enter two strings.\n"); gets(s1); gets(s2); check(s1,s2,p); } void check(char *a,char *b,int (*cmp)(const char *,const char *)) { printf("Testing for equality.\n"); //表达式(*cmp)(a,b)调用strcmp,由cmp指向库函数strcmp(),由a和b作调用strcmp()的参数。 //调用时,与声明的情况类似,必须在*cmp周围使用一对括号,使编译程序正确操作, //同时这也是一种良好的编码风格,指示函数是通过指针调用的,而不是函数名。 if((*cmp)(a,b)==0) printf("Equal\n"); else printf("Not Equal\n"); }
int *f(int i, int j);
int (*p)(int i, int j);
前者是返回值是指针的函数;后者是一个指向函数的指针。
注意 本实验用到的技巧也将会在JIT编译器(如,浏览器中的JavaScript编译器)中出现,因为它们也是将数据转换为代码,然后再调用。请注意,试图执行代码的攻击可能会使用本实验室的变体。这个实验也说明了一个常见的错误,可能是不确定的。
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
代码
#include <stdlib.h> #include <string.h> #include <strings.h> #include <stdio.h> // example of qsort using function pointer for comparison function (*compar)() // see man qsort char *num[] = { "000000001", "1", "1000", " 100 ", "1 ", }; // compare p1 & p2 as strings // call with the address of the array element, e.g. &(char *) = char ** int string_comp(const void *p1, const void *p2) { // be careful that address of element is passed so there is an // extra * needed here given that it is already (char *) // return strcmp((char *) p1, (char *) p2); /* a bug as needs deref */ return strcmp(*((char **) p1), *((char **) p2)); } // compare p1 & p2 as integers int int_comp(const void *p1, const void *p2) { int i1, i2; // i1 = atoi((char *) p1); /* bug: same reason as line in string_comp() */ i1 = atoi(*((char **) p1)); /* correct */ // i2 = atoi((char *) p2); /* bug: same reason as line in string_comp() */ i2 = atoi(*((char **) p2)); /* correct */ // printf("comp(%s,%s)\n", p1, p2); /* bug: for debugging */ // printf("comp(\"%s\", \"%s\")\n", *(char **) p1, *(char **) p2); /* correct: for debugging */ if (i1 < i2) return -1; else if (i1 == i2) return 0; else return 1; } void print_array(char *a[], int n) { int i; for (i=0; i < n; i++) printf("\"%s\" ", a[i]); } int main() { printf("Original array\n"); print_array(num, 5); printf("\n"); qsort(num, 5, sizeof(char *), int_comp); printf("sorted array as int\n"); print_array(num, 5); printf("\n"); qsort(&num, 5, sizeof(char *), string_comp); printf("sorted array as string\n"); print_array(num, 5); printf("\n"); return(0); }
一般形式:strcmp(字符串1,字符串2)
说明:当s1<s2时,返回为负数 注意不是-1
当s1==s2时,返回值= 0
当s1>s2时,返回正数 注意不是1
即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:
“A”<“B” “a”>“A” “computer”>“compare”
特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,不能比较数字等其他形式的参数。
代码:
#include <stdio.h> #include <string.h> #include <strings.h> #include <errno.h> #include <sys/mman.h> #define L (32) int addnum(int a) { return a+255; } int main(int argc, char *argv[], char *envp[]) { int a, (*f)(int), *code_buf; char *p, data[]={0x0f, 0x0b}; /* Try uncommenting out */ /* f = NULL; a = f(10); printf("0: f(10)=%d f=%p\n\n", a, f); */ f = addnum; a = f(10); // LINE1 printf("1: f(10)=%d f=%p\n\n", a, f); code_buf = (int *) mmap(0, 4096, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); /* Try swapping the two mmap lines */ // code_buf = (int *) mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); printf("code_buf %p\n", code_buf); memcpy(code_buf, addnum, L); f = (int (*)(int)) code_buf; a = f(10); // LINE2 printf("2: f(10)=%d f=%p\n\n", a, f); p = index((char *) code_buf, 0xff); printf("before *p=%hhx\n", *p); *p = 100; printf("after *p=%hhx\n", *p); a = f(10); // LINE3 printf("3: f(10)=%d\n\n", a); *((char *) code_buf) = 0xc3; a = f(10); // LINE4 printf("4: f(10)=%d\n\n", a); memcpy(code_buf, data, 2); printf("before last call\n"); a = f(10); // LINE5 return 0; }
#include <sys/mman.h>
void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);
fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。
offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值:若映射成功则__返回映射区的内存起始地址__,否则返回MAP_FAILED(-1),错误原因存于errno 中。
错误代码:
EBADF 参数fd 不是有效的文件描述词
EACCES 存取权限有误。如果是M
P_PRIVATE 情况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。
EINVAL 参数start、length 或offset有一个不合法。
EAGAIN 文件被锁住,或是有太多内存被锁住。
ENOMEM 内存不足。
代码:
#include <stdio.h> #include <string.h> #include <strings.h> #include <errno.h> #include <sys/mman.h> int encrypt(int a) // simple encryption with a (secret) constant { return a ^ 255; } int main(int argc, char *argv[], char *envp[]) { int (*f)(int); int secret, x, y; // test encrypt f = encrypt; printf("1: enter test data\n"); scanf("%d", &x); y = f(x); printf("1: test original encrypt(): f(%x)=%x\n", x, y); printf("enter new key\n"); scanf("%d", &secret); // create a new function by using encrypt's code-bytes as a template // which is similar to encrypt() except that it is xor with the secret // key which has been input // let f point to the new function created by the code below // LINE1 /* your code goes here */ // LINE2 secret = 255; // erase secret, set it back to the original key // test if it works printf("2: value to encrypt\n"); scanf("%d", &x); y = f(x); // should use the input secret key above printf("2: f(): f(%x)=%x\n", x, y); printf("erased secret %d\n", secret); // test if f() is modifiable *((int *)f) = 0; // LINE3: must segfault here return 0; }