__free_hook 劫持原理

源码来自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
$