Post

(kernel) hook sys\_call\_table

hook sys_call_table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <linux/module.h>    // included for all kernel modules
#include <linux/kernel.h>    // included for KERN\_INFO
#include <linux/init.h>      // included for \_\_init and \_\_exit macros
#include <linux/cred.h>


MODULE\_LICENSE("GPL");
MODULE\_AUTHOR("umbum");
MODULE\_DESCRIPTION("sys\_call\_table hooking test");

  

#define page\_offset 0xc0000000
#define phys\_offset 0x1a000000

  

// #define \_\_NR\_write 4
#define \_\_NR\_target 4

  

typedef asmlinkage long (\*sys\_write\_t)(unsigned int fd, const char \_\_user \*buf, size\_t count);

  

sys\_write\_t orig\_sys\_write;
unsigned long \*\*sct;

  

asmlinkage long hook\_sys\_write(unsigned int fd, char \_\_user \*buf, size\_t count){
if (!strcmp(buf, "bum")){
buf[0] = 'B';  // not string, but char
buf[1] = 'U';  // so, these are not affected by addr\_limit
buf[2] = 'M';
}

  

return orig\_sys\_write(fd, buf, count);
}

  

static unsigned long \*\*get\_sys\_call\_table(void){
return page\_offset + phys\_offset + 0x005d3160; // sct\_offset
}

  


  

static void set\_page\_rw(unsigned long addr, int bit){
unsigned int level;
pte\_t \*pte;

  

pte = lookup\_address(addr, &level);
if (bit == 1) {
pte->pte |= \_PAGE\_RW;
}
else {
pte->pte &= ~\_PAGE\_RW;
}
}

  


  

static int \_\_init b\_hook\_init(void)
{
sct = get\_sys\_call\_table();

  

printk(KERN\_INFO "b\_hook LKM is loaded\n");
printk("origin   sct[\_\_NR\_target]  %p : %p : %p\n", &sct[\_\_NR\_target], sct[\_\_NR\_target], \*sct[\_\_NR\_target]);

  

orig\_sys\_write = (sys\_write\_t)sct[\_\_NR\_target];
set\_page\_rw((unsigned long)sct, 1);
sct[4] = (unsigned long \*)hook\_sys\_write;

  

printk("hooked   sct[\_\_NR\_target]  %p : %p : %p\n", &sct[\_\_NR\_target], sct[\_\_NR\_target], \*sct[\_\_NR\_target]);

  

return 0;    // Non-zero return means that the module couldn't be loaded.
}


static void \_\_exit b\_hook\_exit(void)
{
sct[\_\_NR\_target] = (unsigned long \*)orig\_sys\_write;
set\_page\_rw((unsigned long)sct, 0);
printk("restored sct[\_\_NR\_target]  %p : %p : %p\n", &sct[\_\_NR\_target], sct[\_\_NR\_target], \*sct[\_\_NR\_target]);
printk(KERN\_INFO "b\_hook LKM is removed.\n");
}


module\_init(b\_hook\_init);
module\_exit(b\_hook\_exit);
  • LKM make 시 타입을 꽤 엄격하게 따지기 때문에, type casting 잘 해주어야 한다.
  • buf의 내용을 수정하기 위해 hook\_sys_write()의 인자에 c const를 제거했는데, 잘 동작한다. 이런 식으로 실제로는 상수가 아니지만, 함수 내부에서 변경하지 못하도록 설정하고 싶은 경우 const를 활용할 수 있을 것 같다.
  • hook\_sys_write()내부에 있는 c printk()가 제대로 로그를 찍어주지 않는다. 단순히 dmesg로 확인이 안될 뿐 버퍼에 쌓이는 건가 싶었는데 rmmod하고 난 이후에도 로그가 찍히지 않는걸로 보아 그냥 유실되는 것 같다.

asmlinkage를 지정하면 어셈블리 코드에서 해당 함수를 직접 호출할 수 있다. 컴파일 시 함수의 인자 전달 방식이 레지스터를 사용하는 방식으로 지정될 경우, assembly code에서 이 함수를 호출할 때 레지스터를 사용해 인자를 전달하도록 코딩하지 않는다면 함수에서 인자를 제대로 받을 수 없다. 따라서 모든 인자를 스택으로 받도록 지정하는 것이 asmlinkage다.

This post is licensed under CC BY 4.0 by the author.