多种Hook技术记录
TODO: 待补充和更新
PRELOAD
Linux基础的hook方式,修改环境变量的LD_PRELOAD=xxx.so优先载入自己的so动态库。
- so对应的源文件里编写与hook目标同名且原型相同的函数,此后的控制流会优先转到该函数中。
- 可以利用
dlopen和dlsym获取到原始函数的地址(比如要hook的是lib内的函数),这样可以实现在调用末尾返回到原始函数中。
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
typedef int(*strcmp_prototype)(const char *, const char *);
typedef strcmp_prototype strcmp_func;
// try to hook strcmp
int strcmp(const char *s1, const char *s2)
{
void *handle = NULL;
strcmp_func original_func = NULL;
if (!handle)
{
handle = dlopen("libc.so.6", RTLD_LAZY);
// oh use RTLD_NEXT to load it
original_func = (strcmp_func)dlsym(handle, "strcmp");
}
printf("Nice Hack\n s1[%s]\n s2[%s]\n", s1, s2);
return original_func(s1, s2);
}
目标
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
if (strcmp(argv[0], "password"))
printf("Incorrect \n");
else
printf("Success\n");
return 0;
}
编译+运行
gcc -shared -o libhook.so -ldl -rdynamic -fPIC hook.c
gcc target.c
LD_PRELOAD=./libhook.so ./a.out
Nice Hack
s1[./a.out]
s2[password]
Incorrect
常见的应用场景应该是网络类工具,可以做一个日志输出,或者类似proxychains对socket进行封装。
Ref
Dynamic Library
原理为上述中第二点,结合dlopen和dlsym获取函数名。这个技术的常见用例就是插件系统。大体思路是设计好API,使用Posix中的dlopen或则windows上的LoadLibrary进行实现。目前我尚未找到一个比较现代化的泛用的插件开发框架(或者辅助工具?),插件常与具体的框架整合在一起,例如Qt、COM这类。我看到一个比较可行的方法是嵌入某类脚本语言的解释器,以脚本语言作为插件开发语言,减轻插件开发者的压力。
基础的头文件,包含规定的API,程序主体与插件都需要包含该头文件。
/*
NAME : plugin.h
DESC : define Plugin APi
*/
#ifndef PROJ_PLUGIN_H
#define PROJ_PLUGIN_H
#include <stdio.h>
#include <string.h>
// pack possible paramter used in Plugin
// about context info or sth ...
// for example
/* struct ParamObjStruct
{
// ... skip
size_t data;
}
typedef ParamObjStruct *ParamObj;
*/
// It is optional, u can also unpack it as func's args
typedef struct
{
char *name;
char *plugin_filename;
size_t version;
} PluginInfoStruct;
typedef PluginInfoStruct *PluginInfo;
// define the prototype of plugin funtion
// can be different types
// I define 2 API here
typedef void(*pluginServiceA)(int);
typedef void *(*pluginServiceB)();
// for better readability
typedef pluginServiceA PLUGIN_API_A;
typedef pluginServiceB PLUGIN_API_B;
#endif
插件系统的头文件和伪实现如下
/**
* NAME: plugin_sys.h
* DESC: functions about plugin system
**/
#ifndef PROJ_PLUGIN_SYS_H
#define PROJ_PLUGIN_SYS_H
#include <dlfcn.h>
#include <stdlib.h>
#include "plugin.h"
#define MAX_PLUGIN_NUM 16
// a global var to store plugin file handle and plugin name
extern char **PLUGIN_NAME_LIST;
extern void **PLUGIN_HANDLER_LIST;
extern int PLUGIN_NO;
enum BOOL_ENUM
{FALSE,TRUE};
typedef enum BOOL_ENUM BOOL;
// you can also declare and implement a function that
// 1. scan a given plugin path (we can also specify an ext name for out plugins)
// and register them all
// such as "void scanPlugins(const char *path);"
// 2. or get plugin info from a config file
// register plugin into a place
BOOL registerPlugin(PluginInfo pluginInfo);
// load plugin and invoke service
void *loadPluginByIndex(const int id);
void invokePluginServiceA(void *handler, size_t data);
void *invokePluginServiceB(void *handler);
// plugin system init
void plugin_sys_init();
void plugin_sys_close();
#endif
/**
NAME : plugin_sys.c
DESC : plugin system implementation
**/
#include "plugin_sys.h"
char **PLUGIN_NAME_LIST;
void **PLUGIN_HANDLER_LIST;
int PLUGIN_NO;
// unpack pluginInfo and store id/name in PLUGIN_NAME_LIST and PLUGIN_HADNLER_LIST
BOOL registerPlugin(PluginInfo pluginInfo)
{
// open the plugin filename
void *handle = NULL;
handle = dlopen(pluginInfo->plugin_filename, RTLD_NOW);
if (handle == NULL)
{
printf("[*]Reg Plugin <%s>:<%s> Error: %s\n",
pluginInfo->name,
pluginInfo->plugin_filename,
dlerror());
return FALSE;
}
int name_len = strlen(pluginInfo->name);
char *plugin_name = (char *)malloc(sizeof(char) * (name_len + 1));
strncpy(plugin_name, pluginInfo->name, name_len);
plugin_name[name_len] = '\0';
PLUGIN_NAME_LIST[PLUGIN_NO] = plugin_name;
PLUGIN_HANDLER_LIST[PLUGIN_NO] = handle;
PLUGIN_NO += 1;
printf("[*]Load plugin : %s, version : %d\n", pluginInfo->name, pluginInfo->version);
return TRUE;
}
// load plugin and invoke service
void *loadPluginByIndex(const int id)
{
printf("[*]Load Handler\n");
return PLUGIN_HANDLER_LIST[id];
}
// transfer name to index, we won't implement it
// void *loadPluginByName(const char *name);
// wrapper function to invoke services inside plugin
void invokePluginServiceA(void *handler, size_t data)
{
PLUGIN_API_A func = (PLUGIN_API_A)dlsym(handler, "serviceA");
func(data);
}
void *invokePluginServiceB(void *handler)
{
PLUGIN_API_B func = (PLUGIN_API_B)dlsym(handler, "serviceB");
func();
}
// plugin system init
void plugin_sys_init()
{
PLUGIN_NAME_LIST = (char **)malloc(sizeof(void *) * MAX_PLUGIN_NUM);
PLUGIN_HANDLER_LIST = (void **)malloc(sizeof(void *) * MAX_PLUGIN_NUM);
// record current plugin no.
PLUGIN_NO = 0;
}
void plugin_sys_close()
{
printf("[-]Start To close\n");
if (PLUGIN_NAME_LIST != NULL)
{
for(int i = 0; i < PLUGIN_NO; ++i)
free(PLUGIN_NAME_LIST[i]);
free(PLUGIN_NAME_LIST);
PLUGIN_NAME_LIST = NULL;
}
if (PLUGIN_HANDLER_LIST != NULL)
{
for (int i = 0; i < PLUGIN_NO; ++i)
dlclose(PLUGIN_HANDLER_LIST[i]);
free(PLUGIN_HANDLER_LIST);
PLUGIN_HANDLER_LIST = NULL;
}
PLUGIN_NO = 0;
printf("[-]Close\n");
}
接下来我们实现一个插件
/*
NAME : example_plugin.c
DESC : a demo to show plugin arch
*/
#include "plugin.h"
#include <stdio.h>
void serviceA(size_t data)
{
printf("[Example Plugin]--Service A printf data : %d\n", data);
}
void *serviceB()
{
printf("[Example Plugin]--Service B printf nothing and return NULL\n");
return NULL;
}
测试文件如下
#include "plugin_sys.h"
#include "plugin.h"
int main(int argc, char *argv[])
{
// init a plugin info for test
PluginInfo info = (PluginInfo)malloc(sizeof(PluginInfoStruct));
info->name = (char*)"Example Plugin";
info->plugin_filename = (char *)"example.plg";
info->version = 1;
// init plugin system
plugin_sys_init();
// register our example plugin
if (TRUE == registerPlugin(info))
{
void *handler = loadPluginByIndex(0);
invokePluginServiceA(handler, 5);
invokePluginServiceB(handler);
}
// close and clean
plugin_sys_close();
free(info);
}
makefile
plugin_sys: main.o plugin_sys.o
gcc -o plugin_sys main.o plugin_sys.o -ldl
main.o: main.c plugin.h plugin_sys.h
gcc -c -g main.c -ldl
plugin_sys.o: plugin_sys.c plugin_sys.h plugin.h
gcc -c -g plugin_sys.c -ldl
clean:
rm *.o
演示
# compile out test case
>> make
>> make clean
# compile our plugin
>> gcc -shared -o example.plg -ldl example_plugin.c
# run and set temp LD LIBRARY
>> LD_LIBRARY_PATH=. ./plugin_sys
[*]Load plugin : Example Plugin, version : 1
[*]Load Handler
[Example Plugin]--Service A printf data : 5
[Example Plugin]--Service B printf nothing and return NULL
[-]Start To close
[-]Close
Ref
- StackOverflow 对于C/C++用于插件设计的讨论
- StackOverflow上插件设计的最佳实践讨论,主要集中于概念上的问题
- 经典文章-CPlusPlus,例子小容易快速理解,探讨C/C++插件ABI中Compiler的影响–Making a plugin system
- [Dr.Dobbs的文章,太长加网站太慢看不下去,匆匆掠过part1]
- An example Plugin Framework - Pluga
- Linux Dynamic的较为全面的介绍–By 郑瀚
Trace Hook
- ltrace
- strace
- fstrace
- ptrace
Ref
Code Injection / Redirection Trick
本章节首要目的为帮助我理解PLT和GOT的概念
其次,Code Injection的原理就是利用mmap将lib.so映射到进程空间中,根据mmap加载的基址加上段偏移计算加载后的地址,用该地址替换PLT表中跳转地址即可。可以用GDB进行实验(需要较高级权限)
Ref
1. Code Injection into Linux Application – 最初的例子考虑的是daemon热更新场景