LIEF
LIEF可以很方便的对elf, pe,MachO 文件进行parse和patch,由于其提供了python库,故可以实现跨平台。
这里以Android平台为例进行测试。
直接修改目标elf的导入符号
注:直接修改符号一般适用于修改前后参数类型、个数完全相同的情况
二进制文件代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <stdio.h>
int main()
{
printf("id");
}
/*
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lief_test
LOCAL_SRC_FILES := main.c
LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE
include $(BUILD_EXECUTABLE)
*/
编译完成后,push到手机中,运行如下图:
使用lief,直接将导入表中的printf符号替换为system1
2
3
4
5import lief
binary = lief.parse("lief_test")
puts_sym = filter(lambda e: e.name == "printf", binary.dynamic_symbols)[0]
puts_sym.name = "system"
binary.write("lief_test_patch");
将lief_test_patch push到手机中,执行结果如下图:
修改 libc 中的相关符号,然后使用 LD_LIBRARY_PATH 加载修改后的库
这里需要搞清楚elf加载的lib位置,例如我在一个64bit手机上做实验,那么加载的lib位于/system/lib64/libc.so, 32bit的libc.so位于/system/lib/libc.so
使用LD_LIBRARY_PATH=xxx 指定动态链接库的路径
先上实例代码:1
2
3
4
5
6
7
int main()
{
puts("id");
printf("finished\n");
return 0;
}
这里要实现libc中的puts和system互换,使用lief1
2
3
4
5
6
7
8
9import lief
libc = lief.parse("libc.so")
system_sym = filter(lambda e: e.name == "system", libc.dynamic_symbols)[0]
puts_sym = filter(lambda e: e.name == "puts", libc.dynamic_symbols)[0]
//注意不能出现两个一样的symbol
puts_sym.name = "system"
system_sym.name = "puts"
libc.write("libc_patch");
将patch后的libc_patch重命名为libc.so,并与要执行的elf文件放在同一目录内,更改其链接库的path并执行1
2
3
4adb push libc_patch /data/local/tmp
cd /data/local/tmp
mv libc_patch libc.so
LD_LIBRARY_PATH=. ./lief_test
执行结果如下图所示:
直接添加代码
经测试printf,system等函数好像不行,会报segment fault错误,待研究
示例代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <a> \n", argv[0]);
exit(-1);
}
int a = atoi(argv[1]);
printf("exp(%d) = %f\n", a, exp(a));
return 0;
}
目标是hook exp 函数,直接增加一个 segments , 然后劫持函数指针到这里。首先编译一个 lib 用来提供用于 hook 的代码。1
2
3
4
5
6
7
8
9
10
11
12
13//test.c
double hook(double x) {
return x + 100;
}
/*
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hook
LOCAL_SRC_FILES := test.c
#LOCAL_CFLAGS += -pic -fPIC
#LOCAL_LDFLAGS += -pic -fPIC
include $(BUILD_SHARED_LIBRARY)
*/
exp函数在libm库中,因此将手机中的/system/lib/libm.so导出来1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import lief
libm = lief.parse("libm.so")
hook = lief.parse("libhook.so")
segment_added = libm.add(hook.segments[0])
print("Hook inserted at VA: 0x{:06x}".format(segment_added.virtual_address))
exp_symbol = libm.get_symbol("exp")
hook_symbol = hook.get_symbol("hook")
exp_symbol.value = segment_added.virtual_address + hook_symbol.value
libm.write("libm.so.6")
运行结果如下图所示:
通过 got/plt 表 直接劫持程序
未测试成功
https://lief-project.github.io/doc/latest/tutorials/05_elf_infect_plt_got.html
修改一个可执行文件为链接库
对于一个PIE的elf,如果想直接调用其中的某个函数,可以通过lief将其修改为library,通过dlopen/dlsym来调用。
1 | #include <stdlib.h> |
用IDA打开crackme101.bin,找到check函数的偏移地址,将其修改为导出函数:1
2
3
4import lief
crackme101 = lief.parse("./crackme101.bin")
crackme101.add_exported_function(0x72A, "check_found")
crackme101.write("libcrackme101.so")
此时的libcrackme101.so既是一个library,又是一个可执行文件
实现对其调用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
typedef int(*check_t)(char*);
int main (int argc, char** argv) {
void* handler = dlopen("./libcrackme101.so", RTLD_LAZY);
check_t check_found = (check_t)dlsym(handler, "check_found");
int output = check_found(argv[1]);
printf("Output of check_found('%s'): %d\n", argv[1], output);
return 0;
}
参考文档: