深入理解Os--调用劫持

1.调用劫持
以Linux系统为例,介绍三种可实现调用劫持的技术。

1.1.编译时调用劫持
以一个实例展开介绍
深入理解Os--调用劫持_第1张图片
(1).main.cpp

#include 
#include 
int main()
{
	int* p = (int*)malloc(32);
	free(p);
	return (0);
}

(2).mymalloc.cpp

#include 
#include 
void* mymalloc(size_t nS)
{
    printf("mymalloc\n");
    return malloc(nS);
}

void myfree(void* p)
{
    printf("myfree\n");
    free(p);
}

(3).malloc.h

#pragma once
#define malloc(nS) mymalloc(nS)
#define free(ptr) myfree(ptr)
void* mymalloc(size_t);
void myfree(void*);

(4).makefile

t2 : t1
	g++ -std=c++11 -I. main.cpp mymalloc.o

t1 :
	g++ -std=c++11 mymalloc.cpp -c

clean :
	rm *.o *.out

这样我们编译得到mymalloc.o过程中,mymalloc.cpp的头文件malloc.h将在默认搜索路径中搜索。这样搜索得到的malloc,free将是 c 库中定义的符号。

我们编译main.cpp为main.o过程中由于指定了-I.,所以,会优先在当前目录下搜索main.cpp中的头文件,这样将采用我们自定义的malloc.h,这样main.cpp中对malloc,free的引用将由于宏替换变为引用mymalloc,myfree。这样在基于main.o,mymalloc.o链接得到可执行程序a.out的过程中,main.o中对mymalloc,myfree的引用将关联到mymalloc.o中的定义。mymalloc.o中对malloc,free的引用将关联到c库中的符号定义。进而达到了调用劫持效果。

这里为了达到引用劫持,我们需要干预main.cpp的编译过程。

1.2.链接时劫持
以一个实例展开介绍
深入理解Os--调用劫持_第2张图片
(1). main.cpp

#include 
#include 

int main()
{
    int *p = (int*)malloc(32);
    free(p);
    return 0;
}

(2). mymalloc.cpp

#include 
extern "C" void *__real_malloc(size_t size);
extern "C" void __real_free(void *ptr);
extern "C" void *__wrap_malloc(size_t size)
{
    void *ptr = __real_malloc(size);
    printf("malloc(%d)=%p\n", (int)size, ptr);
    return ptr;
}

extern "C" void __wrap_free(void *ptr)
{
    __real_free(ptr);
    printf("free(%p)\n", ptr);
}

(3). makefile

t : t1 t2
	g++ -std=c++11 -Wl,--wrap,malloc -Wl,--wrap,free main.o mymalloc.o
t1 :
	g++ -std=c++11 mymalloc.cpp -c
t2 :
	g++ -std=c++11 main.cpp -c

我们编译得到mymalloc.o的过程中,按常规方式编译即可。注意,这里对引用的符号__real_malloc__real_free和定义的符号__wrap_malloc__wrap_free均加了extern "C"修饰,这是为了使得通过c++编译器编译时,放置符号名被编译器重新命名。

我们编译得到main.o的过程中,按常规方式编译即可。

我们之所以可以劫持main.cpp中对malloc,free的调用,是因为通过main.o,mymalloc.o链接得到可执行程序过程中我们使用了-Wl,--wrap,malloc-Wl,--wrap,free编译选项。

-Wl,--wrap,malloc可以达到的效果是,将所有引用malloc的地方替换为对__wrap_malloc的引用。将所有引用__real_malloc的地方替换为对malloc的引用。
-Wl,--wrap,free可以达到的效果是,将所有引用free的地方替换为对__wrap_free的引用。将所有引用__real_free的地方替换为对free的引用。

这样通过上述引用替换后,重定位阶段main.o中将分别重定位到对mymalloc.o中定义符号的引用。mymalloc.o中将分别重定位到对 C 库中定义符号的引用。

这里为了达到引用劫持,我们需要干预得到可执行程序的链接过程。

1.3.运行时劫持
以一个实例展开介绍
深入理解Os--调用劫持_第3张图片
(1). main.cp

#include 
#include 

int main()
{
    int *p = (int*)malloc(32);
    free(p);
    return 0;
}

(2).mymalloc.cpp

#include 
#include 
#include 

typedef void *(*Mallocp)(size_t size);
typedef void (*Freep)(void*);
void* malloc(size_t size)
{
    Mallocp mallocp;
    char *error;
    mallocp = (Mallocp)dlsym(RTLD_NEXT, "malloc");
    if((error = dlerror()) != NULL)
    {
        fputs(error, stderr);
        exit(1);
    }

    char *ptr = (char*)mallocp(size);
    printf("malloc(%d) = %p\n", (int)size, ptr);
    return ptr;
}

void free(void *ptr)
{
    Freep freep;
    char *error;
    if(!ptr)
        return;
    freep = (Freep)dlsym(RTLD_NEXT, "free");
    if((error = dlerror()) != NULL)
    {
        fputs(error, stderr);
        exit(1);
    }

    freep(ptr);
    printf("free(%p)\n", ptr);
}

(3).makefile

t : t1
	g++ -std=c++11 main.cpp
t1 :
	g++ -shared -fpic -o mymalloc.so mymalloc.cpp -ldl

上述过程,我们维持可执行程序编译链接的过程。
我们通过一个额外的动态库,这个动态库里面对我们想劫持的调用提供了新的定义。
(4).s.sh

export LD_PRELOAD="./mymalloc.so" 
./a.out

我们通过在启动可执行进程前,设置LD_PRELOAD,使得,可执行程序执行前首先加载LD_PRELOAD中指定的动态库。
这样,后续可执行程序加载运行时,需要执行对动态链接符号的重定位时,将优先搜索LD_PRELOAD中的库是否提供了符号定义,若提供,则采用库中定义的版本。这样,main.cpp中对malloc,free的引用会被重定位到我们提供的动态库中的版本。
我们动态库中想使用c库中定义的malloc,free时,采用dlsym的方式。通过参数 1 指定RTLD_NEXT来使得跳过LD_PRELOAD库中的定义,继续向后寻找符号定义,最终将其关联到 c 库中的符号定义。

这里为了达到引用劫持,无需干预可执行程序的编译链接过程。只需在启动脚本设置LD_PRELOAD并提供实现了想劫持调用的动态库即可。具有最大的灵活性,最易于实现。

你可能感兴趣的:(3.1.系统-理论,调用劫持,编译时劫持,链接时劫持,运行时劫持)