# linux hook
之前有分享 linux hook的方式,包括ld_preload,目前有個情況需要額外override 原本的某個function在linux 環境,這邊記錄一下可行方案
# lief
https://github.com/lief-project/LIEF
```bash=
pip install lief
```
如果採用直接修改elf 是否有可能直接hook 原來的function
直接看官方文檔
https://lief-project.github.io/doc/latest/tutorials/04_elf_hooking.html
https://lief-project.github.io/doc/latest/tutorials/05_elf_infect_plt_got.html
我們直接對比較重要的這兩個範例紀錄一下
https://github.com/lief-project/tutorials
作者的github project連結
# 04_elf_hooking
## do_math.c
```c=
#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("cos(%d) = %f\n", a, cos(a));
return 0;
}
```
## hook.c
```c=
double hook(double x) {
return x + 1;
//return (double)((int)x + 3);
}
```
## insert_hook.py
```
#!/usr/bin/env python3
# Description
# -----------
# Hook the 'cos' function from the standard math library (libm)
import lief
libm = lief.parse("/usr/lib/libm.so.6")
hook = lief.parse("hook")
cos_symbol = libm.get_symbol("cos")
hook_symbol = hook.get_symbol("hook")
code_segment = hook.segment_from_virtual_address(hook_symbol.value)
segment_added = libm.add(code_segment)
print("Hook inserted at VA: 0x{:06x}".format(segment_added.virtual_address))
# Offset of the function 'hook' within the CODE segment
hook_offset = hook_symbol.value - code_segment.virtual_address
new_addr = segment_added.virtual_address + hook_offset
print(f"Change {cos_symbol.name}!{cos_symbol.value:x} -> {cos_symbol.name}!{new_addr:x}")
cos_symbol.value = new_addr
libm.write("libm.so.6")
```
到這邊do_math 所加載的 數學函式庫 cos 已經被替換成hook function
# 05_elf_infect_plt_got.html
這一個範例來說
## crackme.c
```c=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Damn_YoU_Got_The_Flag
char password[] = "\x18\x3d\x31\x32\x03\x05\x33\x09\x03\x1b\x33\x28\x03\x08\x34\x39\x03\x1a\x30\x3d\x3b";
inline int check(char* input);
int check(char* input) {
for (int i = 0; i < sizeof(password) - 1; ++i) {
password[i] ^= 0x5c;
}
return memcmp(password, input, sizeof(password) - 1);
}
int main(int argc, char **argv) {
if (argc != 2) {
printf("Usage: %s <password>\n", argv[0]);
return EXIT_FAILURE;
}
if (strlen(argv[1]) == (sizeof(password) - 1) && check(argv[1]) == 0) {
puts("You got it !!");
return EXIT_SUCCESS;
}
puts("Wrong");
return EXIT_FAILURE;
}
```
## hook.c
```c=
#include "arch/x86_64/syscall.c"
#define stdout 1
int my_memcmp(const void* lhs, const void* rhs, int n) {
const char msg[] = "Hook add\n";
_write(stdout, msg, sizeof(msg));
_write(stdout, (const char*)lhs, n);
_write(stdout, "\n", 2);
_write(stdout, (const char*)rhs, n);
_write(stdout, "\n", 2);
return 0;
}
```
## hook_pltgot.py
```python=
# ./crackme.hooked XXXXXXXXXXXXXXXXXXXXX
# Hook add
# Damn_YoU_Got_The_Flag
# XXXXXXXXXXXXXXXXXXXXX
# You got it !!
import lief
crackme = lief.parse("crackme.bin")
hook = lief.parse("hook")
segment_added = crackme.add(hook.segments[0])
my_memcmp = hook.get_symbol("my_memcmp")
my_memcmp_addr = segment_added.virtual_address + my_memcmp.value
crackme.patch_pltgot('memcmp', my_memcmp_addr)
# Remove bind now if present
if lief.ELF.DYNAMIC_TAGS.FLAGS in crackme:
flags = crackme[lief.ELF.DYNAMIC_TAGS.FLAGS]
flags.remove(lief.ELF.DYNAMIC_FLAGS.BIND_NOW)
if lief.ELF.DYNAMIC_TAGS.FLAGS_1 in crackme:
flags = crackme[lief.ELF.DYNAMIC_TAGS.FLAGS_1]
flags.remove(lief.ELF.DYNAMIC_FLAGS_1.NOW)
# Remove RELRO
if lief.ELF.SEGMENT_TYPES.GNU_RELRO in crackme:
crackme[lief.ELF.SEGMENT_TYPES.GNU_RELRO].type = lief.ELF.SEGMENT_TYPES.NULL
crackme.write("crackme.hooked")
```
直接把memcmp 轉為 my_memcmp 間接orverride掉function ,試了幾次好像沒辦法替換掉libc 的 function
# ld_preload
```
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <dlfcn.h>
static int (*orig_printf)(const char *format, ...) = NULL;
int printf(const char *format, ...)
{
if (orig_printf == NULL)
{
orig_printf = (int (*)(const char *format, ...))dlsym(RTLD_NEXT, "printf");
}
orig_printf("within my own printf\n");
// TODO: print desired message from caller.
return orig_printf("within my own printf\n");
}
```
```bash=
gcc -Wall -fPIC -shared -o PrintfHank.so PrintfHank.c -ldl
export LD_PRELOAD=./PrintfHank.so
```
![](https://i.imgur.com/QhTo86o.png)
大概整理了這些方式,主要要針對沒辦法修改的程式碼進行額外處理
算是重新封裝printf ,讓輸出log可以濾掉
必須注意
https://stackoverflow.com/questions/1576689/problems-on-injecting-into-printf-using-ld-preload-method
有時候假設printf沒有帶參數可能會被優化成puts
printf
```
printf("rko%d\n",123);
```
puts
```
printf("rko\n",123);
```
# example
```c=
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdarg.h>
static int (*orig_printf)(const char *format, ...) = NULL;
static int (*orig_puts)(const char *str) = NULL;
int puts(const char *str)
{
if (orig_puts == NULL)
{
orig_puts = (int (*)(const char *str))dlsym(RTLD_NEXT, "puts");
}
return orig_puts("within my own puts");
}
int printf(const char *format, ...)
{
if (orig_printf == NULL)
{
orig_printf = (int (*)(const char *format, ...))dlsym(RTLD_NEXT, "printf");
}
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
char *ch;
ch =arg;
orig_printf("%s\n",ch);
va_end (arg);
return orig_printf("within my own printf\n");
}
```
# ptrace
當然使用 ptrace 也可以
## override printf
```c=
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/syscall.h>
void hook_printf() {
printf("printf has been intercepted!\n");
}
int main() {
pid_t child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, 0, 0);
execl("./a.out", "24", 0);
}
else {
wait(NULL);
long orig_eax;
while (1) {
struct user_regs_struct regs;
ptrace(PTRACE_SYSCALL, child, 0, 0);
wait(NULL);
ptrace(PTRACE_GETREGS, child, NULL, ®s);
orig_eax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
if (orig_eax == __NR_write) {
// change the return address to hook_printf
hook_printf();
// read the string from memory
char *str = (char*)regs.rdi;
char buffer[1024];
for (int i = 0; i < regs.rdx; i++) {
buffer[i] = ptrace(PTRACE_PEEKDATA, child, regs.rsi + i, NULL);
}
buffer[regs.rdx] = '\0';
printf("printf2: %s\n", buffer);
}
}
}
return 0;
}
```
## no execute printf
```c=
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/syscall.h>
void hook_printf() {
printf("printf has been intercepted!\n");
}
int main() {
pid_t child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, 0, 0);
execl("./a.out", "24", 0);
}
else {
wait(NULL);
long orig_eax;
while (1) {
struct user_regs_struct regs;
ptrace(PTRACE_SYSCALL, child, 0, 0);
wait(NULL);
ptrace(PTRACE_GETREGS, child, NULL, ®s);
orig_eax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
if (orig_eax == __NR_write) {
// change the return address to hook_printf
hook_printf();
regs.rax = -1;
ptrace(PTRACE_SETREGS, child, NULL, ®s);
printf("printf disabled\n");
// read the string from memory
// char *str = (char*)regs.rdi;
// char buffer[1024];
// for (int i = 0; i < regs.rdx; i++) {
// buffer[i] = ptrace(PTRACE_PEEKDATA, child, regs.rsi + i, NULL);
// }
// buffer[regs.rdx] = '\0';
// printf("printf: %s\n", buffer);
}
}
}
return 0;
}
```
## run exit
```c=
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/syscall.h>
void hook_printf() {
printf("printf has been intercepted!\n");
}
int main() {
pid_t child = fork();
if (child == 0) {
ptrace(PTRACE_TRACEME, 0, 0, 0);
execl("./a.out", "24", 0);
}
else {
int status;
wait(NULL);
long orig_eax;
while (1) {
struct user_regs_struct regs;
ptrace(PTRACE_SYSCALL, child, 0, 0);
// wait(NULL);
if(wait(&status) == -1)
break;
if (WIFEXITED(status)) {
ptrace(PTRACE_DETACH, child, 0, 0);
break;
}
ptrace(PTRACE_GETREGS, child, NULL, ®s);
orig_eax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
if (orig_eax == __NR_write) {
// change the return address to hook_printf
hook_printf();
// regs.rax = -1;
// ptrace(PTRACE_SETREGS, child, NULL, ®s);
// printf("printf disabled\n");
// read the string from memory
char *str = (char*)regs.rdi;
char buffer[1024];
for (int i = 0; i < regs.rdx; i++) {
buffer[i] = ptrace(PTRACE_PEEKDATA, child, regs.rsi + i, NULL);
}
buffer[regs.rdx] = '\0';
printf("printf: %s\n", buffer);
}
}
}
return 0;
}
```
# read arg
```
execl("./a.out", "a.out","XXXXXXXXXX", 0);
```
![](https://i.imgur.com/nrOKWIp.png)
這樣印出來的後綴都有加上想要的字串了,其實glibc 跟之前介紹一樣有malloc hook 和 free hook,ebpf 也可以達成hook不
過需要重編linux kernel 開啟ebpf的模組,使用ld_preload 還是蠻方便的不過權限可能需要實際情況才能確定,
![](https://i.imgur.com/eC6Msia.png)
# 參考
https://axcheron.github.io/playing-with-ld_preload/