源码来自glibc-2.19,具体的行数代码中会标明。实验环境为glibc-2.23。
先看一个简单的例子,下面的代码直接对全局变量__free_hook进行修改。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
extern void (*__free_hook) (void *__ptr,const void *);
int main()
{
char *str = malloc(160);
strcpy(str,"/bin/sh");
printf("__free_hook: 0x%016X\n",__free_hook);
// 劫持__free_hook
__free_hook = system;
free(str);
return 0;
}
结果如下:
ex@ubuntu:~/test$ gcc -o demo -g demo.c
demo.c: In function ‘main’:
demo.c:12:9: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 2 has type ‘void (*)(void *, const void *)’ [-Wformat=]
printf("__free_hook: 0x%016X\n",__free_hook);
^
demo.c:14:14: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
__free_hook = system;
^
ex@ubuntu:~/test$ ./demo
__free_hook: 0x0000000000000000
$ echo hello world
hello world
$
知道了简单的做法,让我们再去源码看看:
glibc-2.19/malloc/malloc.c:1830
void weak_variable (*__free_hook) (void *__ptr,
const void *) = NULL;
这里定义为的__free_hook定义为全局变量,所以可以直接被修改。再来看看__free_hook的引用:
// glibc-2.19/malloc/malloc.c:2913
void
__libc_free (void *mem)
{
mstate ar_ptr;
mchunkptr p; /* chunk corresponding to mem */
void (*hook) (void *, const void *)
= atomic_forced_read (__free_hook);
if (__builtin_expect (hook != NULL, 0))
{
(*hook)(mem, RETURN_ADDRESS (0));
return;
}
上面的代码对是free()函数的一部分,可以看出程序先把全局变量__free_hook赋给了局部变量hook,然后对hook是否为NULL进行判断,如果不为空,则执行hook,第一个参数就是chunk的内容部分。
下面将由具体的代码来演示__free_hook 劫持:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// glibc-2.19/include/atomic.h:541
# define atomic_forced_read(x) \
({ __typeof (x) __x; __asm ("" : "=r" (__x) : "0" (x)); __x; })
// glibc-2.19/include/libc-symbols.h:91
# define weak_function __attribute__ ((weak))
// glibc-2.19/malloc/malloc.c:1818
# define weak_variable weak_function
// glibc-2.19/malloc/malloc.c:1830
void weak_variable (*__free_hook) (void *__ptr,
const void *) = NULL;
int main(int argc,char **argv)
{
char *ptr = NULL;
// hacker
if(argc >= 2)
{
ptr = argv[1];
__free_hook = system;
}
// glibc-2.19/malloc/malloc.c:2914
void (*hook) (void *, const void *)
= atomic_forced_read (__free_hook);
if(hook != NULL)
{
(*hook)(ptr,NULL);
}
return 0;
}
一般的情况下__free_hook是为NULL的,所以是不会执行的,但是如果有人恶意修改__free_hook的话,就会造成__free_hook劫持。情况如下:
ex@ubuntu:~/test$ gcc -g -o demo2 demo2.c
demo2.c: In function ‘main’:
demo2.c:25:15: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
__free_hook = system;
^
ex@ubuntu:~/test$ ./demo2
ex@ubuntu:~/test$ ./demo2 /bin/sh
$ echo hello world
hello world
$