本文为看雪论坛优秀文章
看雪论坛作者ID:爱吃菠菜
(1) GOT_HOOK
(2) LDPRELOAD_HOOK
(3) INLINE_HOOK
(4) 异常HOOK
(5) ELF依赖库篡改注入
(6) LINKER_HOOK
(7) UNICOR仿真器HOOK
(8) 实现代码
(1)硬编码记录导入符号在外部SO中的偏移值,计算外部SO的模块基址相加后得到地址,与GOT表项值进行对比。(2)将硬编码改为动态解析,使用dlsmy()函数或自己实现地址解析后得到地址,与GOT表项值对比。(3)一般在链接后GOT表值不会发生改变,可在初始化完成时,使用前面两个方法逐个校验,然后整体计算此时的GOT表的HASH值,在后期运行时进行HASH校验。(相比逐个校验,提高了执行效率、代码动作更小、增加了隐蔽性,甚至还可选择MD5 CRC以外的迷你HASH、或跳跃字节校验HASH)
有了GOT项下标,符号名,所属库名,再过滤出哪些需要检测的重点GOT项,即可实现检测。STRUCT GOT{
gotitem;
libname;
elfhash_sym_name;
gotitem;
sym_addr;
}GOT;
我没有实际测试过这种HOOK,但可以想到,当导入了虚假的导入函数,GOT表项值会和真正的值不同。
这样的话,检测LDPRELOAD_HOOK就类似检测GOT_HOOK。可以使用检测GOT时的第一种硬编码方法,记录导入函数在导入SO中的偏移量然后加基址进行校验。对于无法获取硬编码偏移量的系统库呢,比如导入了虚假的fopen什么的,我觉得可以预设一个重点库重点函数名单,比如弄一个libc.so的IO之类的高频函数列表,手动算偏移来解决。
通过篡改函数的指令内容实现,应该是最为通用有效的HOOK手段,可以想到,通常的HOOK框架与工具,最后应该都会通过这种原理来实现。去检测框架特征肯定不如检测INLINE_HOOK有效,就是搞起来会比较麻烦。
计算一份函数体的HASH,运行时/调用前/对被调函数做完整性校验。原理并不复杂,但实际工程有很繁琐的问题,场景对灵活性要求很多。C : 什么时机做检测?如何在没有源代码的情况下插入检测代码,做到运行时每次调用前都会执行校验逻辑?( 方案:基于静态动态分析的结果进行HOOK?或者直接VMP?)B : 这些函数有的是非导出没有符号或是C++名称粉碎的,怎么获取它们的地址范围?( 方案:把检测逻辑单独写成一个库,HASH数据也放库里?放在外部配置文件中?在被检测库开辟的BUFFER中?)F: 系统库没法提前知道HASH,被INLINE_HOOK了怎么检测?( 方案:把导入库的libcso符号替换为自己静态编译的libc内容?预设一份高频系统库函数名单在函数体中查跳转特征?)
基础的信息是函数所属库名称、函数的起始结束地址、函数得HASH值:<br>
STRUCT FUNC{
libname; // 所属模块(预设)
head; // 函数起始偏移(预设)
end; // 函数结束偏移(预设)
filehash; // 函数哈希(预设)
md_base; // 所属模块基址(运行时计算)
addr; // 函数虚拟地址(运行时计算)
}FUNC;
我没有实际测试过这种HOOK手段,不过看到异常HOOK也会篡改函数指令内容。所以检测异常HOOK可以使用检测INLINE_HOOK的方式,应该是这样的。
修改ELF中的DYNAMIC结构,添加一个DT_NEEDED,从而增加额外的导入SO。
也是提前计算好SO的关键区段的HASH,运行时再次计算来对比,校验时机的问题,和INLINE_HOOK类似(感觉吧被注入这种事避免不了的,别人HOOK我的代码还能检测检测,别人想往进程里加载SO这个机会可太多了,不改ELF也有N种方法,没什么办法)。这里还有说一些额外的内容,我们既然篡改DYNAMIC区段可以实现一些功能,类似的,其他区段是不是也有薄弱的地方,是不是也可以篡改它们实现某些功能?函数调用这个流程不仅有GOT表,还有PLT表,保护加固校验了GOT表和INLINE,那CRACKER从PLT表下手行不行?HASH表也在函数调用流程中, CRACKER改一下HASH表是不是也能实现HOOK的功能?INIT_ARRAY也在流程中,这块是不是也可以实现一些意想不到得功能?
struct {
SEC dynsym;
SEC dynstr;
SEC hash;
SEC reldyn;
SEC relplt;
SEC plt;
SEC arm_extab;
SEC arm_exidx;
SEC rodata;
}LOAD1;
struct {
SEC fini_array;
SEC init_array;
SEC dynamic;
}LOAD2;<br>
我没有实际测试过这种HOOK手段,不过可以脑补一下,LINKER_HOOK能做什么,应该可以控制GOT表内容,可以添加与HOOK导入函数,还有一些dlopen()相关的事。
CRACKER要对LINKER动手脚,暂时我也没有想到什么好的办法。对于GOT表相关的,还是通过保护GOT表的手段去做。dlopen()相关的工程能力强的话可以自己实现dlopen,在关键行为处使用,甚至替换掉哪些调用系统dlopen的部分。
效果像是HOOK了CPU一样, HOOK中的大杀器、杀手锏,神挡杀神,X谁谁发抖。
基本没有办法,不过可以思考,在工程上提高CRACKER使用UNICOR的复杂度、成本。https://github.com/acbocai/vergil
目前检测代码,写成了一个单独的SO库形式, 需要用户自己在关键逻辑处手动调用,测试可以检测到UNICOR以外的各种hook手段和工具。需要说明的是,依赖的参数需要另行编写工具解析构造。(推荐使用IDAT脚本或LIEF库)
#pragma once
#include <stdint.h>
typedef struct ANTI_HOOK{
uint32_t global_data[48000];
}ANTI_HOOK;
extern "C" bool anti_hook(
ANTI_HOOK* global,
bool is_close,
uint32_t* arr_func,
uint32_t count);
gothook部分逻辑为:通过解析文件实现mydlsym解析出符号地址与got表对比。inlinehook部分逻辑为:用户传入想要检测inlinehook的函数地址size,接着计算其运行时内存的哈希,最后与预设的哈希map进行查找比对。(作者使用idat写了另一脚本工具,可识别出文件中所有有无符号的函数区间,项目可在作者github找到。)https://bbs.pediy.com/thread-254386.htm
system lib的inlinehook部分逻辑为:预设一份libc.so的重点函数表,初始化时计算其文件hash,后期调用时再计算内存hash与文件hash比对。区段检测部分逻辑为:预设了所有可能被动手脚的区段的哈希,在运行时再次计算哈希进行校验,出于效率和隐蔽性考虑,使用了简易的字符串哈希方法,并且隔字节进行计算。
该库本身被HOOK、防绕过、调用检测的问题,是另一个问题,该项目不再展开涉及,可以选择与壳融合,或者静态编译libc方式解决等。检测方法不唯一,应该会有更好的方式,但是作者没发现别人公开类似的工程,只能写一些自己目前想到的检测手段。看雪ID:爱吃菠菜
https://bbs.pediy.com/user-760871.htm
*本文由看雪论坛 爱吃菠菜 原创,转载请注明来自看雪社区进阶安全圈,不得不读的一本书
文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458299656&idx=1&sn=f91656e6add2a8d4b61f753f1ab6228a&chksm=b1819d8286f61494f39461f5a05f7ac59f7d266656d20ce17bab5921ae146ddc4b6414746d35#rd
如有侵权请联系:admin#unsafe.sh